/*
This file is part of Ext JS 6.0.1.250

Copyright (c) 2011-2015 Sencha Inc

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

Commercial Usage
Licensees holding valid commercial licenses may use this file in accordance with the Commercial
Software License Agreement provided with the Software or, alternatively, in accordance with the
terms contained in a written agreement between you and Sencha.

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

Version: 6.0.1.250 Build date: 2015-09-02 16:59:34 (22ef9ff0ebf584ff525541be37e753a703cc044b)

*/
// @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'),
                    len = scriptEls.length,
                    re = /\/ext(\-[a-z\-]+)?\.js$/,
                    entry, script, src, state, baseUrl, key, n, origin;
                
                
                
                for (n = 0; n < len; n++) {
                    src = (script = scriptEls[n]).src;
                    if (!src) {
                        
                        continue;
                    }
                    state = script.readyState || null;
                    
                    if (!baseUrl) {
                        if (re.test(src)) {
                            Boot.hasReadyState = ("readyState" in script);
                            Boot.hasAsync = ("async" in script) || !Boot.hasReadyState;
                            baseUrl = src;
                        }
                    }
                    if (!Boot.scripts[key = Boot.canonicalUrl(src)]) {
                        
                        _debug("creating entry " + key + " in Boot.init");
                        
                        entry = new Entry({
                            key: key,
                            url: src,
                            done: state === null || 
                            state === 'loaded' || state === 'complete',
                            
                            el: script,
                            prop: 'src'
                        });
                    }
                }
                if (!baseUrl) {
                    script = scriptEls[scriptEls.length - 1];
                    baseUrl = script.src;
                    Boot.hasReadyState = ('readyState' in script);
                    Boot.hasAsync = ("async" in script) || !Boot.hasReadyState;
                }
                Boot.baseUrl = baseUrl.substring(0, baseUrl.lastIndexOf('/') + 1);
                origin = window.location.origin || window.location.protocol + "//" + window.location.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) {
                var key = Boot.canonicalUrl(url),
                    entry = Boot.scripts[key];
                if (!entry) {
                    entry = Boot.create(url, key, cfg);
                }
                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) {
                
                _debug("Boot.load called");
                
                var request = new Request(request);
                if (request.sync || Boot.syncMode) {
                    return Boot.loadSync(request);
                }
                
                
                if (Boot.currentRequest) {
                    
                    _debug("current active request, suspending this request");
                    
                    
                    
                    
                    request.getEntries();
                    Boot.suspendedQueue.push(request);
                } else {
                    Boot.currentRequest = request;
                    Boot.processRequest(request, false);
                }
                return Boot;
            },
            loadSync: function(request) {
                
                _debug("Boot.loadSync called");
                
                var request = new Request(request);
                Boot.syncMode++;
                Boot.processRequest(request, true);
                Boot.syncMode--;
                return Boot;
            },
            loadBasePrefix: function(request) {
                request = new Request(request);
                request.prependBaseUrl = true;
                return Boot.load(request);
            },
            loadSyncBasePrefix: function(request) {
                request = new Request(request);
                request.prependBaseUrl = true;
                return Boot.loadSync(request);
            },
            requestComplete: function(request) {
                var next;
                if (Boot.currentRequest === request) {
                    Boot.currentRequest = null;
                    while (Boot.suspendedQueue.length > 0) {
                        next = Boot.suspendedQueue.shift();
                        if (!next.done) {
                            
                            _debug("resuming suspended request");
                            
                            Boot.load(next);
                            break;
                        }
                    }
                }
                if (!Boot.currentRequest && Boot.suspendedQueue.length == 0) {
                    Boot.fireListeners();
                }
            },
            isLoading: function() {
                return !Boot.currentRequest && Boot.suspendedQueue.length == 0;
            },
            fireListeners: function() {
                var listener;
                while (Boot.isLoading() && (listener = Boot.listeners.shift())) {
                    listener();
                }
            },
            onBootReady: function(listener) {
                if (!Boot.isLoading()) {
                    listener();
                } else {
                    Boot.listeners.push(listener);
                }
            },
            
            getPathsFromIndexes: function(indexMap, loadOrder) {
                return Request.prototype.getPathsFromIndexes(indexMap, loadOrder);
            },
            createLoadOrderMap: function(loadOrder) {
                return Request.prototype.createLoadOrderMap(loadOrder);
            },
            fetch: function(url, complete, scope, async) {
                async = (async === undefined) ? !!complete : async;
                var xhr = new XMLHttpRequest(),
                    result, status, content,
                    exception = false,
                    readyStateChange = function() {
                        if (xhr && xhr.readyState == 4) {
                            status = (xhr.status === 1223) ? 204 : (xhr.status === 0 && ((self.location || {}).protocol === 'file:' || (self.location || {}).protocol === 'ionp:')) ? 200 : xhr.status;
                            content = xhr.responseText;
                            result = {
                                content: content,
                                status: status,
                                exception: exception
                            };
                            if (complete) {
                                complete.call(scope, result);
                            }
                            xhr = null;
                        }
                    };
                if (async) {
                    xhr.onreadystatechange = readyStateChange;
                }
                try {
                    
                    _debug("fetching " + url + " " + (async ? "async" : "sync"));
                    
                    xhr.open('GET', url, async);
                    xhr.send(null);
                } catch (err) {
                    exception = err;
                    readyStateChange();
                    return result;
                }
                if (!async) {
                    readyStateChange();
                }
                return result;
            },
            notifyAll: function(entry) {
                entry.notifyRequests();
            }
        };
    
    function Request(cfg) {
        if (cfg.$isRequest) {
            return cfg;
        }
        var cfg = cfg.url ? cfg : {
                url: cfg
            },
            url = cfg.url,
            urls = url.charAt ? [
                url
            ] : url,
            charset = cfg.charset || Boot.config.charset;
        _apply(cfg, {
            urls: urls,
            charset: charset
        });
        _apply(this, cfg);
    }
    
    Request.prototype = {
        $isRequest: true,
        
        createLoadOrderMap: function(loadOrder) {
            var len = loadOrder.length,
                loadOrderMap = {},
                i, element;
            for (i = 0; i < len; i++) {
                element = loadOrder[i];
                loadOrderMap[element.path] = element;
            }
            return loadOrderMap;
        },
        
        getLoadIndexes: function(index, indexMap, loadOrder, includeUses, skipLoaded) {
            var item = loadOrder[index],
                len, i, reqs, entry, stop, added, idx, ridx, url;
            if (indexMap[index]) {
                
                return indexMap;
            }
            indexMap[index] = true;
            stop = false;
            while (!stop) {
                added = false;
                
                
                for (idx in indexMap) {
                    if (indexMap.hasOwnProperty(idx)) {
                        item = loadOrder[idx];
                        if (!item) {
                            
                            continue;
                        }
                        url = this.prepareUrl(item.path);
                        entry = Boot.getEntry(url);
                        if (!skipLoaded || !entry || !entry.done) {
                            reqs = item.requires;
                            if (includeUses && item.uses) {
                                reqs = reqs.concat(item.uses);
                            }
                            for (len = reqs.length , i = 0; i < len; i++) {
                                ridx = reqs[i];
                                
                                
                                
                                
                                if (!indexMap[ridx]) {
                                    indexMap[ridx] = true;
                                    added = true;
                                }
                            }
                        }
                    }
                }
                
                
                if (!added) {
                    stop = true;
                }
            }
            return indexMap;
        },
        getPathsFromIndexes: function(indexMap, loadOrder) {
            var indexes = [],
                paths = [],
                index, len, i;
            for (index in indexMap) {
                if (indexMap.hasOwnProperty(index) && indexMap[index]) {
                    indexes.push(index);
                }
            }
            indexes.sort(function(a, b) {
                return a - b;
            });
            
            for (len = indexes.length , i = 0; i < len; i++) {
                paths.push(loadOrder[indexes[i]].path);
            }
            return paths;
        },
        expandUrl: function(url, indexMap, includeUses, skipLoaded) {
            if (typeof url == 'string') {
                url = [
                    url
                ];
            }
            var me = this,
                loadOrder = me.loadOrder,
                loadOrderMap = me.loadOrderMap;
            if (loadOrder) {
                loadOrderMap = loadOrderMap || me.createLoadOrderMap(loadOrder);
                me.loadOrderMap = loadOrderMap;
                indexMap = indexMap || {};
                var len = url.length,
                    unmapped = [],
                    i, item;
                for (i = 0; i < len; i++) {
                    item = loadOrderMap[url[i]];
                    if (item) {
                        me.getLoadIndexes(item.idx, indexMap, loadOrder, includeUses, skipLoaded);
                    } else {
                        unmapped.push(url[i]);
                    }
                }
                return me.getPathsFromIndexes(indexMap, loadOrder).concat(unmapped);
            }
            return url;
        },
        expandUrls: function(urls, includeUses) {
            if (typeof urls == "string") {
                urls = [
                    urls
                ];
            }
            var expanded = [],
                expandMap = {},
                tmpExpanded,
                len = urls.length,
                i, t, tlen, tUrl;
            for (i = 0; i < len; i++) {
                tmpExpanded = this.expandUrl(urls[i], {}, includeUses, true);
                for (t = 0 , tlen = tmpExpanded.length; t < tlen; t++) {
                    tUrl = tmpExpanded[t];
                    if (!expandMap[tUrl]) {
                        expandMap[tUrl] = true;
                        expanded.push(tUrl);
                    }
                }
            }
            if (expanded.length == 0) {
                expanded = urls;
            }
            return expanded;
        },
        expandLoadOrder: function() {
            var me = this,
                urls = me.urls,
                expanded;
            if (!me.expanded) {
                expanded = this.expandUrls(urls, true);
                me.expanded = true;
            } else {
                expanded = urls;
            }
            me.urls = expanded;
            
            
            if (urls.length != expanded.length) {
                me.sequential = true;
            }
            return me;
        },
        getUrls: function() {
            this.expandLoadOrder();
            return this.urls;
        },
        prepareUrl: function(url) {
            if (this.prependBaseUrl) {
                return Boot.baseUrl + url;
            }
            return url;
        },
        getEntries: function() {
            var me = this,
                entries = me.entries,
                i, entry, urls, url;
            if (!entries) {
                entries = [];
                urls = me.getUrls();
                for (i = 0; i < urls.length; i++) {
                    url = me.prepareUrl(urls[i]);
                    entry = Boot.getEntry(url, {
                        buster: me.buster,
                        charset: me.charset
                    });
                    entry.requests.push(me);
                    entries.push(entry);
                }
                me.entries = entries;
            }
            return entries;
        },
        loadEntries: function(sync) {
            var me = this,
                entries = me.getEntries(),
                len = entries.length,
                start = me.loadStart || 0,
                continueLoad, entry, i;
            if (sync !== undefined) {
                me.sync = sync;
            }
            me.loaded = me.loaded || 0;
            me.loading = me.loading || len;
            for (i = start; i < len; i++) {
                entry = entries[i];
                if (!entry.loaded) {
                    continueLoad = entries[i].load(me.sync);
                } else {
                    continueLoad = true;
                }
                if (!continueLoad) {
                    me.loadStart = i;
                    entry.onDone(function() {
                        me.loadEntries(sync);
                    });
                    break;
                }
            }
            me.processLoadedEntries();
        },
        processLoadedEntries: function() {
            var me = this,
                entries = me.getEntries(),
                len = entries.length,
                start = me.startIndex || 0,
                i, entry;
            if (!me.done) {
                for (i = start; i < len; i++) {
                    entry = entries[i];
                    if (!entry.loaded) {
                        me.startIndex = i;
                        return;
                    }
                    if (!entry.evaluated) {
                        entry.evaluate();
                    }
                    if (entry.error) {
                        me.error = true;
                    }
                }
                me.notify();
            }
        },
        notify: function() {
            var me = this;
            if (!me.done) {
                var error = me.error,
                    fn = me[error ? 'failure' : 'success'],
                    delay = ('delay' in me) ? me.delay : (error ? 1 : Boot.config.chainDelay),
                    scope = me.scope || me;
                me.done = true;
                if (fn) {
                    if (delay === 0 || delay > 0) {
                        
                        setTimeout(function() {
                            fn.call(scope, me);
                        }, delay);
                    } else {
                        fn.call(scope, me);
                    }
                }
                me.fireListeners();
                Boot.requestComplete(me);
            }
        },
        onDone: function(listener) {
            var me = this,
                listeners = me.listeners || (me.listeners = []);
            if (me.done) {
                listener(me);
            } else {
                listeners.push(listener);
            }
        },
        fireListeners: function() {
            var listeners = this.listeners,
                listener;
            if (listeners) {
                
                _debug("firing request listeners");
                
                while ((listener = listeners.shift())) {
                    listener(this);
                }
            }
        }
    };
    
    function Entry(cfg) {
        if (cfg.$isEntry) {
            return cfg;
        }
        
        _debug("creating entry for " + cfg.url);
        
        var charset = cfg.charset || Boot.config.charset,
            manifest = Ext.manifest,
            loader = manifest && manifest.loader,
            cache = (cfg.cache !== undefined) ? cfg.cache : (loader && loader.cache),
            buster, busterParam;
        if (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(cfg, {
            charset: charset,
            buster: buster,
            requests: []
        });
        _apply(this, cfg);
    }
    
    Entry.prototype = {
        $isEntry: true,
        done: false,
        evaluated: false,
        loaded: false,
        isCrossDomain: function() {
            var me = this;
            if (me.crossDomain === undefined) {
                
                _debug("checking " + me.getLoadUrl() + " for prefix " + Boot.origin);
                
                me.crossDomain = (me.getLoadUrl().indexOf(Boot.origin) !== 0);
            }
            return me.crossDomain;
        },
        isCss: function() {
            var me = this;
            if (me.css === undefined) {
                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) {
                
                _debug("creating element for " + me.url);
                
                if (me.isCss()) {
                    tag = tag || "link";
                    el = doc.createElement(tag);
                    if (tag == "link") {
                        el.rel = 'stylesheet';
                        me.prop = 'href';
                    } else {
                        me.prop = "textContent";
                    }
                    el.type = "text/css";
                } else {
                    tag = tag || "script";
                    el = doc.createElement(tag);
                    el.type = 'text/javascript';
                    me.prop = 'src';
                    if (me.charset) {
                        el.charset = me.charset;
                    }
                    if (Boot.hasAsync) {
                        el.async = false;
                    }
                }
                me.el = el;
            }
            return el;
        },
        getLoadUrl: function() {
            var me = this,
                url = Boot.canonicalUrl(me.url);
            if (!me.loadUrl) {
                me.loadUrl = !!me.buster ? (url + (url.indexOf('?') === -1 ? '?' : '&') + me.buster) : url;
            }
            return me.loadUrl;
        },
        fetch: function(req) {
            var url = this.getLoadUrl(),
                async = !!req.async,
                complete = req.complete;
            Boot.fetch(url, complete, this, async);
        },
        onContentLoaded: function(response) {
            var me = this,
                status = response.status,
                content = response.content,
                exception = response.exception,
                url = this.getLoadUrl();
            me.loaded = true;
            if ((exception || status === 0) && !_environment.phantom) {
                me.error = 
                ("Failed loading synchronously via XHR: '" + url + "'. It's likely that the file is either being loaded from a " + "different domain or from the local file system where cross " + "origin requests are not allowed for security reasons. Try " + "asynchronous loading instead.") || 
                true;
                me.evaluated = true;
            } else if ((status >= 200 && status < 300) || status === 304 || _environment.phantom || (status === 0 && content.length > 0)) {
                me.content = content;
            } else {
                me.error = 
                ("Failed loading synchronously via XHR: '" + url + "'. Please verify that the file exists. XHR status code: " + status) || 
                true;
                me.evaluated = true;
            }
        },
        createLoadElement: function(callback) {
            var me = this,
                el = me.getElement(),
                readyStateChange = function() {
                    if (this.readyState === 'loaded' || this.readyState === 'complete') {
                        if (callback) {
                            callback();
                        }
                    }
                },
                errorFn = function() {
                    me.error = true;
                    if (callback) {
                        callback();
                    }
                };
            me.preserve = true;
            el.onerror = errorFn;
            if (Boot.hasReadyState) {
                el.onreadystatechange = readyStateChange;
            } else {
                el.onload = callback;
            }
            
            el[me.prop] = me.getLoadUrl();
        },
        onLoadElementReady: function() {
            Boot.getHead().appendChild(this.getElement());
            this.evaluated = true;
        },
        inject: function(content, asset) {
            
            _debug("injecting content for " + this.url);
            
            var me = this,
                head = Boot.getHead(),
                url = me.url,
                key = me.key,
                base, el, ieMode, basePath;
            if (me.isCss()) {
                me.preserve = true;
                basePath = key.substring(0, key.lastIndexOf("/") + 1);
                base = doc.createElement('base');
                base.href = basePath;
                if (head.firstChild) {
                    head.insertBefore(base, head.firstChild);
                } else {
                    head.appendChild(base);
                }
                
                base.href = base.href;
                if (url) {
                    content += "\n/*# sourceURL=" + key + " */";
                }
                
                el = me.getElement("style");
                ieMode = ('styleSheet' in el);
                head.appendChild(base);
                if (ieMode) {
                    head.appendChild(el);
                    el.styleSheet.cssText = content;
                } else {
                    el.textContent = content;
                    head.appendChild(el);
                }
                head.removeChild(base);
            } else {
                
                
                
                if (url) {
                    content += "\n//# sourceURL=" + key;
                }
                Ext.globalEval(content);
            }
            return me;
        },
        loadCrossDomain: function() {
            var me = this,
                complete = function() {
                    me.loaded = me.evaluated = me.done = true;
                    me.notifyRequests();
                };
            me.createLoadElement(function() {
                complete();
            });
            me.evaluateLoadElement();
            
            
            
            return false;
        },
        loadElement: function() {
            var me = this,
                complete = function() {
                    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 (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) {
                
                _debug("firing event listeners for url " + this.url);
                
                while ((listener = listeners.shift())) {
                    listener(this);
                }
            }
        }
    };
    
    Ext.disableCacheBuster = function(disable, path) {
        var date = new Date();
        date.setTime(date.getTime() + (disable ? 10 * 365 : -1) * 24 * 60 * 60 * 1000);
        date = date.toGMTString();
        doc.cookie = 'ext-cache=1; expires=' + date + '; path=' + (path || '/');
    };
    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 = function() {},
        privateFn = function() {},
        identityFn = function(o) {
            return o;
        },
        
        
        callOverrideParent = function() {
            var method = callOverrideParent.caller.caller;
            
            return method.$owner.prototype[method.$name].apply(this, arguments);
        },
        manifest = Ext.manifest || {},
        i,
        iterableRe = /\[object\s*(?:Array|Arguments|\w*Collection|\w*List|HTML\s+document\.all\s+class)\]/,
        MSDateRe = /^\\?\/Date\(([-+])?(\d+)(?:[+-]\d{4})?\)\\?\/$/;
    Ext.global = global;
    
    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';
    
    
    Ext['suspendLayouts'] = Ext['resumeLayouts'] = emptyFn;
    
    for (i in {
        toString: 1
    }) {
        enumerables = null;
    }
    
    Ext.enumerables = enumerables;
    
    Ext.apply = function(object, config, defaults) {
        if (defaults) {
            Ext.apply(object, defaults);
        }
        if (object && config && typeof config === 'object') {
            var i, j, k;
            for (i in config) {
                object[i] = config[i];
            }
            if (enumerables) {
                for (j = enumerables.length; j--; ) {
                    k = enumerables[j];
                    if (config.hasOwnProperty(k)) {
                        object[k] = config[k];
                    }
                }
            }
        }
        return object;
    };
    
    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-',
        
        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,
        
        enableAriaButtons: true,
        
        enableAriaPanels: 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(),
        
        
        baseCSSPrefix: Ext.buildSettings.baseCSSPrefix,
        
        $eventNameMap: {},
        
        
        $vendorEventRe: /^(Moz.+|MS.+|webkit.+)/,
        
        
        canonicalEventName: function(name) {
            return Ext.$eventNameMap[name] || (Ext.$eventNameMap[name] = (Ext.$vendorEventRe.test(name) ? name : name.toLowerCase()));
        },
        
        applyIf: function(object, config) {
            var property;
            if (object) {
                for (property in config) {
                    if (object[property] === undefined) {
                        object[property] = config[property];
                    }
                }
            }
            return object;
        },
        
        destroy: function() {
            var ln = arguments.length,
                i, arg;
            for (i = 0; i < ln; i++) {
                arg = arguments[i];
                if (arg) {
                    if (Ext.isArray(arg)) {
                        this.destroy.apply(this, arg);
                    } else if (Ext.isFunction(arg.destroy)) {
                        arg.destroy();
                    }
                }
            }
            return null;
        },
        
        destroyMembers: function(object) {
            for (var ref, name,
                i = 1,
                a = arguments,
                len = a.length; i < len; i++) {
                ref = object[name = a[i]];
                
                if (ref != null) {
                    object[name] = Ext.destroy(ref);
                }
            }
        },
        
        override: function(target, overrides) {
            if (target.$isClass) {
                target.override(overrides);
            } else if (typeof target === 'function') {
                Ext.apply(target.prototype, overrides);
            } else {
                var owner = target.self,
                    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(value) {
            return toString.call(value) === '[object Date]';
        },
        
        isMSDate: function(value) {
            if (!Ext.isString(value)) {
                return false;
            }
            return MSDateRe.test(value);
        },
        
        isObject: (toString.call(null) === '[object Object]') ? function(value) {
            
            return value !== null && value !== undefined && toString.call(value) === '[object Object]' && value.ownerDocument === undefined;
        } : function(value) {
            return toString.call(value) === '[object Object]';
        },
        
        isSimpleObject: function(value) {
            return value instanceof Object && value.constructor === Object;
        },
        
        isPrimitive: function(value) {
            var type = typeof value;
            return type === 'string' || type === 'number' || type === 'boolean';
        },
        
        isFunction: 
        
        (typeof document !== 'undefined' && typeof document.getElementsByTagName('body') === 'function') ? function(value) {
            return !!value && toString.call(value) === '[object Function]';
        } : function(value) {
            return !!value && typeof value === 'function';
        },
        
        isNumber: function(value) {
            return typeof value === 'number' && isFinite(value);
        },
        
        isNumeric: function(value) {
            return !isNaN(parseFloat(value)) && isFinite(value);
        },
        
        isString: function(value) {
            return typeof value === 'string';
        },
        
        isBoolean: function(value) {
            return typeof value === 'boolean';
        },
        
        isElement: function(value) {
            return value ? value.nodeType === 1 : false;
        },
        
        isTextNode: function(value) {
            return value ? value.nodeName === "#text" : false;
        },
        
        isDefined: function(value) {
            return typeof value !== 'undefined';
        },
        
        isIterable: function(value) {
            
            if (!value || typeof value.length !== 'number' || typeof value === 'string' || Ext.isFunction(value)) {
                return false;
            }
            
            
            
            if (!value.propertyIsEnumerable) {
                return !!value.item;
            }
            
            
            if (value.hasOwnProperty('length') && !value.propertyIsEnumerable('length')) {
                return true;
            }
            
            return iterableRe.test(toString.call(value));
        },
        
        isDebugEnabled: 
        function(className, defaultEnabled) {
            var debugConfig = Ext.debugConfig.hooks;
            if (debugConfig.hasOwnProperty(className)) {
                return debugConfig[className];
            }
            var enabled = debugConfig['*'],
                prefixLength = 0;
            if (defaultEnabled !== undefined) {
                enabled = defaultEnabled;
            }
            if (!className) {
                return enabled;
            }
            for (var prefix in debugConfig) {
                var value = debugConfig[prefix];
                
                if (className.charAt(prefix.length) === '.') {
                    if (className.substring(0, prefix.length) === prefix) {
                        if (prefixLength < prefix.length) {
                            prefixLength = prefix.length;
                            enabled = value;
                        }
                    }
                }
            }
            return enabled;
        } || 
        emptyFn,
        
        clone: function(item) {
            if (item === null || item === undefined) {
                return item;
            }
            
            
            
            if (item.nodeType && item.cloneNode) {
                return item.cloneNode(true);
            }
            var type = toString.call(item),
                i, j, k, clone, key;
            
            if (type === '[object Date]') {
                return new Date(item.getTime());
            }
            
            if (type === '[object Array]') {
                i = item.length;
                clone = [];
                while (i--) {
                    clone[i] = Ext.clone(item[i]);
                }
            }
            
            else if (type === '[object Object]' && item.constructor === Object) {
                clone = {};
                for (key in item) {
                    clone[key] = Ext.clone(item[key]);
                }
                if (enumerables) {
                    for (j = enumerables.length; j--; ) {
                        k = enumerables[j];
                        if (item.hasOwnProperty(k)) {
                            clone[k] = item[k];
                        }
                    }
                }
            }
            return clone || item;
        },
        
        getUniqueGlobalNamespace: function() {
            var uniqueGlobalNamespace = this.uniqueGlobalNamespace,
                i;
            if (uniqueGlobalNamespace === undefined) {
                i = 0;
                do {
                    uniqueGlobalNamespace = 'ExtBox' + (++i);
                } while (global[uniqueGlobalNamespace] !== undefined);
                global[uniqueGlobalNamespace] = Ext;
                this.uniqueGlobalNamespace = uniqueGlobalNamespace;
            }
            return uniqueGlobalNamespace;
        },
        
        functionFactoryCache: {},
        cacheableFunctionFactory: function() {
            var me = this,
                args = Array.prototype.slice.call(arguments),
                cache = me.functionFactoryCache,
                idx, fn, ln;
            if (Ext.isSandboxed) {
                ln = args.length;
                if (ln > 0) {
                    ln--;
                    args[ln] = 'var Ext=window.' + Ext.name + ';' + args[ln];
                }
            }
            idx = args.join('');
            fn = cache[idx];
            if (!fn) {
                fn = Function.prototype.constructor.apply(Function.prototype, args);
                cache[idx] = fn;
            }
            return fn;
        },
        functionFactory: function() {
            var args = Array.prototype.slice.call(arguments),
                ln;
            if (Ext.isSandboxed) {
                ln = args.length;
                if (ln > 0) {
                    ln--;
                    args[ln] = 'var Ext=window.' + Ext.name + ';' + args[ln];
                }
            }
            return Function.prototype.constructor.apply(Function.prototype, args);
        },
        
        Logger: {
            
            log: function(message, priority) {
                if (message && global.console) {
                    if (!priority || !(priority in global.console)) {
                        priority = 'log';
                    }
                    message = '[' + priority.toUpperCase() + '] ' + message;
                    global.console[priority](message);
                }
            },
            verbose: function(message) {
                this.log(message, 'verbose');
            },
            info: function(message) {
                this.log(message, 'info');
            },
            warn: function(message) {
                this.log(message, 'warn');
            },
            error: function(message) {
                throw new Error(message);
            },
            deprecate: function(message) {
                this.log(message, 'warn');
            }
        } || {
            
            verbose: emptyFn,
            log: emptyFn,
            info: emptyFn,
            warn: emptyFn,
            error: function(message) {
                throw new Error(message);
            },
            deprecate: emptyFn
        },
        
        getElementById: function(id) {
            return document.getElementById(id);
        },
        
        splitAndUnescape: (function() {
            var cache = {};
            return function(origin, delimiter) {
                if (!origin) {
                    return [];
                } else if (!delimiter) {
                    return [
                        origin
                    ];
                }
                var replaceRe = cache[delimiter] || (cache[delimiter] = new RegExp('\\\\' + delimiter, 'g')),
                    result = [],
                    parts, part;
                parts = origin.split(delimiter);
                while ((part = parts.shift()) !== undefined) {
                    
                    
                    while (part.charAt(part.length - 1) === '\\' && parts.length > 0) {
                        part = part + delimiter + parts.shift();
                    }
                    
                    part = part.replace(replaceRe, delimiter);
                    result.push(part);
                }
                return result;
            };
        })()
    });
    
    Ext.returnTrue.$nullFn = Ext.returnId.$nullFn = true;
}());





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



Ext.Array = (function() {
    
    
    
    
    var arrayPrototype = Array.prototype,
        slice = arrayPrototype.slice,
        supportsSplice = (function() {
            var array = [],
                lengthBefore,
                j = 20;
            if (!array.splice) {
                return false;
            }
            
            
            while (j--) {
                array.push("A");
            }
            array.splice(15, 0, "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F");
            lengthBefore = array.length;
            
            array.splice(13, 0, "XXX");
            
            if (lengthBefore + 1 !== array.length) {
                return false;
            }
            
            return true;
        }()),
        supportsIndexOf = 'indexOf' in arrayPrototype,
        supportsSliceOnNodeList = true;
    
    
    function stableSort(array, userComparator) {
        var len = array.length,
            indices = new Array(len),
            i;
        
        for (i = 0; i < len; i++) {
            indices[i] = i;
        }
        
        indices.sort(function(index1, index2) {
            return userComparator(array[index1], array[index2]) || (index1 - index2);
        });
        
        for (i = 0; i < len; i++) {
            indices[i] = array[indices[i]];
        }
        
        for (i = 0; i < len; i++) {
            array[i] = indices[i];
        }
        return array;
    }
    try {
        
        if (typeof document !== 'undefined') {
            slice.call(document.getElementsByTagName('body'));
        }
    } catch (e) {
        supportsSliceOnNodeList = false;
    }
    var fixArrayIndex = function(array, index) {
            return (index < 0) ? Math.max(0, array.length + index) : Math.min(array.length, index);
        },
        
        replaceSim = function(array, index, removeCount, insert) {
            var add = insert ? insert.length : 0,
                length = array.length,
                pos = fixArrayIndex(array, index);
            
            if (pos === length) {
                if (add) {
                    array.push.apply(array, insert);
                }
            } else {
                var remove = Math.min(removeCount, length - pos),
                    tailOldPos = pos + remove,
                    tailNewPos = tailOldPos + add - remove,
                    tailCount = length - tailOldPos,
                    lengthAfterRemove = length - remove,
                    i;
                if (tailNewPos < tailOldPos) {
                    
                    for (i = 0; i < tailCount; ++i) {
                        array[tailNewPos + i] = array[tailOldPos + i];
                    }
                } else if (tailNewPos > tailOldPos) {
                    
                    for (i = tailCount; i--; ) {
                        array[tailNewPos + i] = array[tailOldPos + i];
                    }
                }
                
                if (add && pos === lengthAfterRemove) {
                    array.length = lengthAfterRemove;
                    
                    array.push.apply(array, insert);
                } else {
                    array.length = lengthAfterRemove + add;
                    
                    for (i = 0; i < add; ++i) {
                        array[pos + i] = insert[i];
                    }
                }
            }
            return array;
        },
        replaceNative = function(array, index, removeCount, insert) {
            if (insert && insert.length) {
                
                if (index === 0 && !removeCount) {
                    array.unshift.apply(array, insert);
                }
                
                else if (index < array.length) {
                    array.splice.apply(array, [
                        index,
                        removeCount
                    ].concat(insert));
                } else 
                {
                    array.push.apply(array, insert);
                }
            } else {
                array.splice(index, removeCount);
            }
            return array;
        },
        eraseSim = function(array, index, removeCount) {
            return replaceSim(array, index, removeCount);
        },
        eraseNative = function(array, index, removeCount) {
            array.splice(index, removeCount);
            return array;
        },
        spliceSim = function(array, index, removeCount) {
            var pos = fixArrayIndex(array, index),
                removed = array.slice(index, fixArrayIndex(array, pos + removeCount));
            if (arguments.length < 4) {
                replaceSim(array, pos, removeCount);
            } else {
                replaceSim(array, pos, removeCount, slice.call(arguments, 3));
            }
            return removed;
        },
        spliceNative = function(array) {
            return array.splice.apply(array, slice.call(arguments, 1));
        },
        erase = supportsSplice ? eraseNative : eraseSim,
        replace = supportsSplice ? replaceNative : replaceSim,
        splice = supportsSplice ? spliceNative : spliceSim,
        
        ExtArray = {
            
            binarySearch: function(array, item, begin, end, compareFn) {
                var length = array.length,
                    middle, comparison;
                if (begin instanceof Function) {
                    compareFn = begin;
                    begin = 0;
                    end = length;
                } else if (end instanceof Function) {
                    compareFn = end;
                    end = length;
                } else {
                    if (begin === undefined) {
                        begin = 0;
                    }
                    if (end === undefined) {
                        end = length;
                    }
                    compareFn = compareFn || ExtArray.lexicalCompare;
                }
                --end;
                while (begin <= end) {
                    middle = (begin + end) >> 1;
                    comparison = compareFn(item, array[middle]);
                    if (comparison >= 0) {
                        begin = middle + 1;
                    } else if (comparison < 0) {
                        end = middle - 1;
                    }
                }
                return begin;
            },
            defaultCompare: function(lhs, rhs) {
                return (lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0);
            },
            
            
            lexicalCompare: function(lhs, rhs) {
                lhs = String(lhs);
                rhs = String(rhs);
                return (lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0);
            },
            
            each: function(array, fn, scope, reverse) {
                array = ExtArray.from(array);
                var i,
                    ln = array.length;
                if (reverse !== true) {
                    for (i = 0; i < ln; i++) {
                        if (fn.call(scope || array[i], array[i], i, array) === false) {
                            return i;
                        }
                    }
                } else {
                    for (i = ln - 1; i > -1; i--) {
                        if (fn.call(scope || array[i], array[i], i, array) === false) {
                            return i;
                        }
                    }
                }
                return true;
            },
            
            forEach: ('forEach' in arrayPrototype) ? function(array, fn, scope) {
                return array.forEach(fn, scope);
            } : function(array, fn, scope) {
                for (var i = 0,
                    ln = array.length; i < ln; i++) {
                    fn.call(scope, array[i], i, array);
                }
            },
            
            indexOf: supportsIndexOf ? function(array, item, from) {
                return arrayPrototype.indexOf.call(array, item, from);
            } : function(array, item, from) {
                var i,
                    length = array.length;
                for (i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++) {
                    if (array[i] === item) {
                        return i;
                    }
                }
                return -1;
            },
            
            contains: supportsIndexOf ? function(array, item) {
                return arrayPrototype.indexOf.call(array, item) !== -1;
            } : function(array, item) {
                var i, ln;
                for (i = 0 , ln = array.length; i < ln; i++) {
                    if (array[i] === item) {
                        return true;
                    }
                }
                return false;
            },
            
            toArray: function(iterable, start, end) {
                if (!iterable || !iterable.length) {
                    return [];
                }
                if (typeof iterable === 'string') {
                    iterable = iterable.split('');
                }
                if (supportsSliceOnNodeList) {
                    return slice.call(iterable, start || 0, end || iterable.length);
                }
                var array = [],
                    i;
                start = start || 0;
                end = end ? ((end < 0) ? iterable.length + end : end) : iterable.length;
                for (i = start; i < end; i++) {
                    array.push(iterable[i]);
                }
                return array;
            },
            
            pluck: function(array, propertyName) {
                var ret = [],
                    i, ln, item;
                for (i = 0 , ln = array.length; i < ln; i++) {
                    item = array[i];
                    ret.push(item[propertyName]);
                }
                return ret;
            },
            
            map: ('map' in arrayPrototype) ? function(array, fn, scope) {
                
                Ext.Assert.isFunction(fn, 'Ext.Array.map must have a callback function passed as second argument.');
                
                return array.map(fn, scope);
            } : function(array, fn, scope) {
                
                Ext.Assert.isFunction(fn, 'Ext.Array.map must have a callback function passed as second argument.');
                
                var results = [],
                    len = array.length,
                    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(array, getKey, scope) {
                var map = {},
                    i = array.length;
                if (!getKey) {
                    while (i--) {
                        map[array[i]] = i + 1;
                    }
                } else if (typeof getKey === 'string') {
                    while (i--) {
                        map[array[i][getKey]] = i + 1;
                    }
                } else {
                    while (i--) {
                        map[getKey.call(scope, array[i])] = i + 1;
                    }
                }
                return map;
            },
            
            toValueMap: function(array, getKey, scope, arrayify) {
                var map = {},
                    i = array.length,
                    autoArray, alwaysArray, entry, fn, key, value;
                if (!getKey) {
                    while (i--) {
                        value = array[i];
                        map[value] = value;
                    }
                } else {
                    if (!(fn = (typeof getKey !== 'string'))) {
                        arrayify = scope;
                    }
                    alwaysArray = arrayify === 1;
                    autoArray = arrayify === 2;
                    while (i--) {
                        value = array[i];
                        key = fn ? getKey.call(scope, value) : value[getKey];
                        if (alwaysArray) {
                            if (key in map) {
                                map[key].push(value);
                            } else {
                                map[key] = [
                                    value
                                ];
                            }
                        } else if (autoArray && (key in map)) {
                            if ((entry = map[key]) instanceof Array) {
                                entry.push(value);
                            } else {
                                map[key] = [
                                    entry,
                                    value
                                ];
                            }
                        } else {
                            map[key] = value;
                        }
                    }
                }
                return map;
            },
            
            _replaceSim: replaceSim,
            
            _spliceSim: spliceSim,
            
            
            erase: erase,
            
            insert: function(array, index, items) {
                return replace(array, index, 0, items);
            },
            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 len = arguments.length,
                    i = 1,
                    newItem;
                if (target === undefined) {
                    target = [];
                } else if (!Ext.isArray(target)) {
                    target = [
                        target
                    ];
                }
                for (; i < len; i++) {
                    newItem = arguments[i];
                    Array.prototype.push[Ext.isIterable(newItem) ? 'apply' : 'call'](target, newItem);
                }
                return target;
            },
            
            numericSortFn: function(a, b) {
                return a - b;
            }
        };
    
    Ext.each = ExtArray.each;
    
    ExtArray.union = ExtArray.merge;
    
    Ext.min = ExtArray.min;
    
    Ext.max = ExtArray.max;
    
    Ext.sum = ExtArray.sum;
    
    Ext.mean = ExtArray.mean;
    
    Ext.flatten = ExtArray.flatten;
    
    Ext.clean = ExtArray.clean;
    
    Ext.unique = ExtArray.unique;
    
    Ext.pluck = ExtArray.pluck;
    
    Ext.toArray = function() {
        return ExtArray.toArray.apply(ExtArray, arguments);
    };
    return ExtArray;
}());






Ext.Assert = {
    
    falsey: function(b, msg) {
        if (b) {
            Ext.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;
        },
        ExtString;
    return ExtString = {
        
        insert: function(s, value, index) {
            if (!s) {
                return value;
            }
            if (!value) {
                return s;
            }
            var len = s.length;
            if (!index && index !== 0) {
                index = len;
            }
            if (index < 0) {
                index *= -1;
                if (index >= len) {
                    
                    index = 0;
                } else {
                    index = len - index;
                }
            }
            if (index === 0) {
                s = value + s;
            } else if (index >= s.length) {
                s += value;
            } else {
                s = s.substr(0, index) + value + s.substr(index);
            }
            return s;
        },
        
        startsWith: function(s, start, ignoreCase) {
            var result = boundsCheck(s, start);
            if (result) {
                if (ignoreCase) {
                    s = s.toLowerCase();
                    start = start.toLowerCase();
                }
                result = s.lastIndexOf(start, 0) === 0;
            }
            return result;
        },
        
        endsWith: function(s, end, ignoreCase) {
            var result = boundsCheck(s, end);
            if (result) {
                if (ignoreCase) {
                    s = s.toLowerCase();
                    end = end.toLowerCase();
                }
                result = s.indexOf(end, s.length - end.length) !== -1;
            }
            return result;
        },
        
        createVarName: function(s) {
            return s.replace(varReplace, '');
        },
        
        htmlEncode: function(value) {
            return (!value) ? value : String(value).replace(charToEntityRegex, htmlEncodeReplaceFn);
        },
        
        htmlDecode: function(value) {
            return (!value) ? value : String(value).replace(entityToCharRegex, htmlDecodeReplaceFn);
        },
        
        hasHtmlCharacters: function(s) {
            return charToEntityRegex.test(s);
        },
        
        addCharacterEntities: function(newEntities) {
            var charKeys = [],
                entityKeys = [],
                key, echar;
            for (key in newEntities) {
                echar = newEntities[key];
                entityToChar[key] = echar;
                charToEntity[echar] = key;
                charKeys.push(echar);
                entityKeys.push(key);
            }
            charToEntityRegex = new RegExp('(' + charKeys.join('|') + ')', 'g');
            entityToCharRegex = new RegExp('(' + entityKeys.join('|') + '|&#[0-9]{1,5};' + ')', 'g');
        },
        
        resetCharacterEntities: function() {
            charToEntity = {};
            entityToChar = {};
            
            this.addCharacterEntities({
                '&amp;': '&',
                '&gt;': '>',
                '&lt;': '<',
                '&quot;': '"',
                '&#39;': "'"
            });
        },
        
        urlAppend: function(url, string) {
            if (!Ext.isEmpty(string)) {
                return url + (url.indexOf('?') === -1 ? '?' : '&') + string;
            }
            return url;
        },
        
        trim: function(string) {
            if (string) {
                string = string.replace(trimRegex, "");
            }
            return string || '';
        },
        
        capitalize: function(string) {
            if (string) {
                string = string.charAt(0).toUpperCase() + string.substr(1);
            }
            return string || '';
        },
        
        uncapitalize: function(string) {
            if (string) {
                string = string.charAt(0).toLowerCase() + string.substr(1);
            }
            return string || '';
        },
        
        ellipsis: function(value, length, word) {
            if (value && value.length > length) {
                if (word) {
                    var vs = value.substr(0, length - 2),
                        index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
                    if (index !== -1 && index >= (length - 15)) {
                        return vs.substr(0, index) + "...";
                    }
                }
                return value.substr(0, length - 3) + "...";
            }
            return value;
        },
        
        escapeRegex: function(string) {
            return string.replace(escapeRegexRe, "\\$1");
        },
        
        createRegex: function(value, startsWith, endsWith, ignoreCase) {
            var ret = value;
            if (value != null && !value.exec) {
                
                ret = ExtString.escapeRegex(String(value));
                if (startsWith !== false) {
                    ret = '^' + ret;
                }
                if (endsWith !== false) {
                    ret += '$';
                }
                ret = new RegExp(ret, (ignoreCase !== false) ? 'i' : '');
            }
            return ret;
        },
        
        escape: function(string) {
            return string.replace(escapeRe, "\\$1");
        },
        
        toggle: function(string, value, other) {
            return string === value ? other : value;
        },
        
        leftPad: function(string, size, character) {
            var result = String(string);
            character = character || " ";
            while (result.length < size) {
                result = character + result;
            }
            return result;
        },
        
        repeat: function(pattern, count, sep) {
            if (count < 1) {
                count = 0;
            }
            for (var buf = [],
                i = count; i--; ) {
                buf.push(pattern);
            }
            return buf.join(sep || '');
        },
        
        splitWords: function(words) {
            if (words && typeof words == 'string') {
                return words.replace(basicTrimRe, '').split(whitespaceRe);
            }
            return words || [];
        }
    };
}());

Ext.String.resetCharacterEntities();

Ext.htmlEncode = Ext.String.htmlEncode;

Ext.htmlDecode = Ext.String.htmlDecode;

Ext.urlAppend = Ext.String.urlAppend;


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


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


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


(function() {
    
    var TemplateClass = function() {},
        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,
                    ln = arguments.length,
                    mergeFn = ExtObject.merge,
                    cloneFn = Ext.clone,
                    object, key, value, sourceKey;
                for (; i < ln; i++) {
                    object = arguments[i];
                    for (key in object) {
                        value = object[key];
                        if (value && value.constructor === Object) {
                            sourceKey = destination[key];
                            if (sourceKey && sourceKey.constructor === Object) {
                                mergeFn(sourceKey, value);
                            } else {
                                destination[key] = cloneFn(value);
                            }
                        } else {
                            destination[key] = value;
                        }
                    }
                }
                return destination;
            },
            
            mergeIf: function(destination) {
                var i = 1,
                    ln = arguments.length,
                    cloneFn = Ext.clone,
                    object, key, value;
                for (; i < ln; i++) {
                    object = arguments[i];
                    for (key in object) {
                        if (!(key in destination)) {
                            value = object[key];
                            if (value && value.constructor === Object) {
                                destination[key] = cloneFn(value);
                            } else {
                                destination[key] = value;
                            }
                        }
                    }
                }
                return destination;
            },
            
            getAllKeys: function(object) {
                var keys = [],
                    property;
                for (property in object) {
                    keys.push(property);
                }
                return keys;
            },
            
            getKey: function(object, value) {
                for (var property in object) {
                    if (object.hasOwnProperty(property) && object[property] === value) {
                        return property;
                    }
                }
                return null;
            },
            
            getValues: function(object) {
                var values = [],
                    property;
                for (property in object) {
                    if (object.hasOwnProperty(property)) {
                        values.push(object[property]);
                    }
                }
                return values;
            },
            
            getKeys: (typeof Object.keys == 'function') ? function(object) {
                if (!object) {
                    return [];
                }
                return Object.keys(object);
            } : function(object) {
                var keys = [],
                    property;
                for (property in object) {
                    if (object.hasOwnProperty(property)) {
                        keys.push(property);
                    }
                }
                return keys;
            },
            
            getSize: function(object) {
                var size = 0,
                    property;
                for (property in object) {
                    if (object.hasOwnProperty(property)) {
                        size++;
                    }
                }
                return size;
            },
            
            isEmpty: function(object) {
                for (var key in object) {
                    if (object.hasOwnProperty(key)) {
                        return false;
                    }
                }
                return true;
            },
            
            equals: (function() {
                var check = function(o1, o2) {
                        var key;
                        for (key in o1) {
                            if (o1.hasOwnProperty(key)) {
                                if (o1[key] !== o2[key]) {
                                    return false;
                                }
                            }
                        }
                        return true;
                    };
                return function(object1, object2) {
                    
                    if (object1 === object2) {
                        return true;
                    }
                    if (object1 && object2) {
                        
                        
                        return check(object1, object2) && check(object2, object1);
                    } else if (!object1 && !object2) {
                        return object1 === object2;
                    } else {
                        return false;
                    }
                };
            })(),
            
            fork: function(obj) {
                var ret, key, value;
                if (obj && obj.constructor === Object) {
                    ret = ExtObject.chain(obj);
                    for (key in obj) {
                        value = obj[key];
                        if (value) {
                            if (value.constructor === Object) {
                                ret[key] = ExtObject.fork(value);
                            } else if (value instanceof Array) {
                                ret[key] = Ext.Array.clone(value);
                            }
                        }
                    }
                } else {
                    ret = obj;
                }
                return ret;
            },
            defineProperty: ('defineProperty' in Object) ? Object.defineProperty : function(object, name, descriptor) {
                if (!Object.prototype.__defineGetter__) {
                    return;
                }
                if (descriptor.get) {
                    object.__defineGetter__(name, descriptor.get);
                }
                if (descriptor.set) {
                    object.__defineSetter__(name, descriptor.set);
                }
            },
            
            classify: function(object) {
                var prototype = object,
                    objectProperties = [],
                    propertyClassesMap = {},
                    objectClass = function() {
                        var i = 0,
                            ln = objectProperties.length,
                            property;
                        for (; i < ln; i++) {
                            property = objectProperties[i];
                            this[property] = new propertyClassesMap[property]();
                        }
                    },
                    key, value;
                for (key in object) {
                    if (object.hasOwnProperty(key)) {
                        value = object[key];
                        if (value && value.constructor === Object) {
                            objectProperties.push(key);
                            propertyClassesMap[key] = ExtObject.classify(value);
                        }
                    }
                }
                objectClass.prototype = prototype;
                return objectClass;
            }
        };
    
    Ext.merge = Ext.Object.merge;
    
    Ext.mergeIf = Ext.Object.mergeIf;
}());


Ext.apply(Ext, {
    
    
    
    
    _namedScopes: {
        'this': {
            isThis: 1
        },
        controller: {
            isController: 1
        },
        
        
        self: {
            isSelf: 1
        },
        'self.controller': {
            isSelf: 1,
            isController: 1
        }
    },
    escapeId: (function() {
        var validIdRe = /^[a-zA-Z_][a-zA-Z0-9_\-]*$/i,
            escapeRx = /([\W]{1})/g,
            leadingNumRx = /^(\d)/g,
            escapeFn = function(match, capture) {
                return "\\" + capture;
            },
            numEscapeFn = function(match, capture) {
                return '\\00' + capture.charCodeAt(0).toString(16) + ' ';
            };
        return function(id) {
            return validIdRe.test(id) ? id : 
            
            id.replace(escapeRx, escapeFn).replace(leadingNumRx, numEscapeFn);
        };
    }()),
    
    callback: function(callback, scope, args, delay, caller, defaultScope) {
        if (!callback) {
            return;
        }
        var namedScope = (scope in Ext._namedScopes);
        if (callback.charAt) {
            
            if ((!scope || namedScope) && caller) {
                scope = caller.resolveListenerScope(namedScope ? scope : defaultScope);
            }
            
            if (!scope || !Ext.isObject(scope)) {
                Ext.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 if (Ext.elevateFunction) {
                ret = Ext.elevateFunction(callback, scope, args);
            } else if (args) {
                ret = callback.apply(scope, args);
            } else {
                ret = callback.call(scope);
            }
        }
        return ret;
    },
    
    coerce: function(from, to) {
        var fromType = Ext.typeOf(from),
            toType = Ext.typeOf(to),
            isString = typeof from === 'string';
        if (fromType !== toType) {
            switch (toType) {
                case 'string':
                    return String(from);
                case 'number':
                    return Number(from);
                case 'boolean':
                    return isString && (!from || from === 'false') ? false : Boolean(from);
                case 'null':
                    return isString && (!from || from === 'null') ? null : from;
                case 'undefined':
                    return isString && (!from || from === 'undefined') ? undefined : from;
                case 'date':
                    return isString && isNaN(from) ? Ext.Date.parse(from, Ext.Date.defaultFormat) : Date(Number(from));
            }
        }
        return from;
    },
    
    copyTo: function(dest, source, names, usePrototypeKeys) {
        if (typeof names === 'string') {
            names = names.split(Ext.propertyNameSplitRe);
        }
        for (var name,
            i = 0,
            n = names ? names.length : 0; i < n; i++) {
            name = names[i];
            if (usePrototypeKeys || source.hasOwnProperty(name)) {
                dest[name] = source[name];
            }
        }
        return dest;
    },
    
    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) {
                for (var m in o) {
                    if (!o.hasOwnProperty(m)) {
                        
                        continue;
                    }
                    this[m] = o[m];
                }
            };
        return function(subclass, superclass, overrides) {
            
            if (Ext.isObject(superclass)) {
                overrides = superclass;
                superclass = subclass;
                subclass = overrides.constructor !== objectConstructor ? overrides.constructor : function() {
                    superclass.apply(this, arguments);
                };
            }
            
            if (!superclass) {
                Ext.raise({
                    sourceClass: 'Ext',
                    sourceMethod: 'extend',
                    msg: 'Attempting to extend from a class which has not been loaded on the page.'
                });
            }
            
            
            var F = function() {},
                subclassProto,
                superclassProto = superclass.prototype;
            F.prototype = superclassProto;
            subclassProto = subclass.prototype = new F();
            subclassProto.constructor = subclass;
            subclass.superclass = superclassProto;
            if (superclassProto.constructor === objectConstructor) {
                superclassProto.constructor = superclass;
            }
            subclass.override = function(overrides) {
                Ext.override(subclass, overrides);
            };
            subclassProto.override = inlineOverrides;
            subclassProto.proto = subclassProto;
            subclass.override(overrides);
            subclass.extend = function(o) {
                return Ext.extend(subclass, o);
            };
            return subclass;
        };
    }()),
    
    iterate: function(object, fn, scope) {
        if (Ext.isEmpty(object)) {
            return;
        }
        if (scope === undefined) {
            scope = object;
        }
        if (Ext.isIterable(object)) {
            Ext.Array.each.call(Ext.Array, object, fn, scope);
        } else {
            Ext.Object.each.call(Ext.Object, object, fn, scope);
        }
    },
    _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');
            div.style.width = div.style.height = '100px';
            div.style.overflow = 'scroll';
            div.style.position = 'absolute';
            db.appendChild(div);
            
            
            Ext._scrollbarSize = scrollbarSize = {
                width: div.offsetWidth - div.clientWidth,
                height: div.offsetHeight - div.clientHeight
            };
            db.removeChild(div);
        }
        return scrollbarSize;
    },
    
    typeOf: (function() {
        var nonWhitespaceRe = /\S/,
            toString = Object.prototype.toString,
            typeofTypes = {
                number: 1,
                string: 1,
                'boolean': 1,
                'undefined': 1
            },
            toStringTypes = {
                '[object Array]': 'array',
                '[object Date]': 'date',
                '[object Boolean]': 'boolean',
                '[object Number]': 'number',
                '[object RegExp]': 'regexp'
            };
        return function(value) {
            if (value === null) {
                return 'null';
            }
            var type = typeof value,
                ret, typeToString;
            if (typeofTypes[type]) {
                return type;
            }
            ret = toStringTypes[typeToString = toString.call(value)];
            if (ret) {
                return ret;
            }
            if (type === 'function') {
                return 'function';
            }
            if (type === 'object') {
                if (value.nodeType !== undefined) {
                    if (value.nodeType === 3) {
                        return nonWhitespaceRe.test(value.nodeValue) ? 'textnode' : 'whitespace';
                    } else {
                        return 'element';
                    }
                }
                return 'object';
            }
            
            Ext.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);
    },
    
    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];
        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.0.1.250');
        Ext.setVersion('core', '6.0.1.250');
    }
})(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;
                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, needsInit, 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) {
                    needsInit = initMap[name];
                    cfg = configs[name];
                    isCached = cfg.cached;
                    if (needsInit) {
                        names = cfg.names;
                        value = values[name];
                        if (!prototype[names.set].$isDefault || prototype[names.apply] || prototype[names.update] || typeof value === 'object') {
                            if (isCached) {
                                
                                
                                
                                
                                
                                (cachedInitList || (cachedInitList = [])).push(cfg);
                            } else {
                                
                                
                                initList.push(cfg);
                                initListMap[name] = true;
                            }
                            
                            
                            
                            instance[names.get] = cfg.initGetter || cfg.getInitGetter();
                        } else {
                            
                            
                            prototype[cfg.getInternalName(prototype)] = value;
                        }
                    } else if (isCached) {
                        prototype[cfg.getInternalName(prototype)] = undefined;
                    }
                }
            }
            
            
            
            ln = cachedInitList && cachedInitList.length;
            if (ln) {
                
                
                
                
                for (i = 0; i < ln; ++i) {
                    internalName = cachedInitList[i].getInternalName(prototype);
                    
                    
                    
                    instance[internalName] = null;
                }
                for (i = 0; i < ln; ++i) {
                    names = (cfg = cachedInitList[i]).names;
                    getter = names.get;
                    if (instance.hasOwnProperty(getter)) {
                        instance[names.set](values[cfg.name]);
                        delete instance[getter];
                    }
                }
                for (i = 0; i < ln; ++i) {
                    internalName = cachedInitList[i].getInternalName(prototype);
                    prototype[internalName] = instance[internalName];
                    delete instance[internalName];
                }
            }
            
            
            
            
            if (instanceConfig && instanceConfig.platformConfig) {
                instanceConfig = me.resolvePlatformConfig(instance, instanceConfig);
            }
            if (firstInstance) {
                
                
                
                if (instance.afterCachedConfig && !instance.afterCachedConfig.$nullFn) {
                    instance.afterCachedConfig(instanceConfig);
                }
            }
            
            
            
            instance.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);
                            }
                        }
                    }
                    values[name] = value;
                }
            }
            
            if (instance.beforeInitConfig && !instance.beforeInitConfig.$nullFn) {
                if (instance.beforeInitConfig(instanceConfig) === false) {
                    return;
                }
            }
            if (instanceConfig) {
                for (name in instanceConfig) {
                    if (!remaining) {
                        
                        
                        break;
                    }
                    
                    
                    
                    cfg = configs[name];
                    if (cfg && !cfg.lazy) {
                        --remaining;
                        
                        names = cfg.names;
                        getter = names.get;
                        
                        
                        
                        
                        if (instance.hasOwnProperty(getter)) {
                            instance[names.set](values[name]);
                            
                            
                            
                            delete instance[names.get];
                        }
                    }
                }
            }
            
            for (i = 0 , ln = initList.length; i < ln; ++i) {
                cfg = initList[i];
                names = cfg.names;
                getter = names.get;
                if (!cfg.lazy && instance.hasOwnProperty(getter)) {
                    
                    
                    
                    
                    instance[names.set](values[cfg.name]);
                    delete instance[getter];
                }
            }
            
            delete instance.isConfiguring;
        },
        getCurrentConfig: function(instance) {
            var defaultConfig = instance.defaultConfig,
                config = {},
                name;
            for (name in defaultConfig) {
                config[name] = instance[configPropMap[name].names.get]();
            }
            return config;
        },
        
        merge: function(instance, baseConfig, config) {
            
            
            var configs = this.configs,
                name, value, baseValue, cfg;
            for (name in config) {
                value = config[name];
                cfg = configs[name];
                if (cfg) {
                    if (cfg.merge) {
                        value = cfg.merge(value, baseConfig[name], instance);
                    } else if (value && value.constructor === Object) {
                        baseValue = baseConfig[name];
                        if (baseValue && baseValue.constructor === Object) {
                            value = Ext.Object.merge(baseValue, value);
                        } else {
                            value = Ext.clone(value);
                        }
                    }
                }
                baseConfig[name] = value;
            }
            return baseConfig;
        },
        
        reconfigure: function(instance, instanceConfig, options) {
            var currentConfig = instance.config,
                configList = [],
                strict = instance.$configStrict && !(options && options.strict === false),
                configs = this.configs,
                defaults = options && options.defaults,
                cfg, getter, i, len, name, names, prop;
            for (name in instanceConfig) {
                if (defaults && instance.hasOwnProperty(name)) {
                    
                    continue;
                }
                currentConfig[name] = instanceConfig[name];
                cfg = configs[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 = [],
        getConfig = function(name, peek) {
            var me = this,
                ret, cfg, getterName;
            if (name) {
                cfg = Ext.Config.map[name];
                
                if (!cfg) {
                    Ext.Logger.error("Invalid property name for getter: '" + name + "' for '" + me.$className + "'.");
                }
                
                getterName = cfg.names.get;
                if (peek && me.hasOwnProperty(getterName)) {
                    ret = me.config[name];
                } else {
                    ret = me[getterName]();
                }
            } else {
                ret = me.getCurrentConfig();
            }
            return ret;
        },
        
        makeDeprecatedMethod = function(oldName, newName, msg) {
            var message = '"' + oldName + '" is deprecated.';
            if (msg) {
                message += ' ' + msg;
            } else if (newName) {
                message += ' Please use "' + newName + '" instead.';
            }
            return function() {
                Ext.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;
    
    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(parent) {
            var me = this,
                parentPrototype = parent.prototype,
                prototype, i, ln, name, statics;
            prototype = me.prototype = Ext.Object.chain(parentPrototype);
            prototype.self = me;
            me.superclass = prototype.superclass = parentPrototype;
            if (!parent.$isClass) {
                for (i in BasePrototype) {
                    if (i in prototype) {
                        prototype[i] = BasePrototype[i];
                    }
                }
            }
            
            
            statics = parentPrototype.$inheritableStatics;
            if (statics) {
                for (i = 0 , ln = statics.length; i < ln; i++) {
                    name = statics[i];
                    if (!me.hasOwnProperty(name)) {
                        me[name] = parent[name];
                    }
                }
            }
            
            if (parent.$onExtended) {
                me.$onExtended = parent.$onExtended.slice();
            }
            
            me.getConfigurator();
        },
        
        
        $onExtended: [],
        
        triggerExtended: function() {
            
            Ext.classSystemMonitor && Ext.classSystemMonitor(this, 'Ext.Base#triggerExtended', arguments);
            
            var callbacks = this.$onExtended,
                ln = callbacks.length,
                i, callback;
            if (ln > 0) {
                for (i = 0; i < ln; i++) {
                    callback = callbacks[i];
                    callback.fn.apply(callback.scope || this, arguments);
                }
            }
        },
        
        onExtended: function(fn, scope) {
            this.$onExtended.push({
                fn: fn,
                scope: scope
            });
            return this;
        },
        
        addStatics: function(members) {
            this.addMembers(members, true);
            return this;
        },
        
        addInheritableStatics: function(members) {
            var inheritableStatics, hasInheritableStatics,
                prototype = this.prototype,
                name, member;
            inheritableStatics = prototype.$inheritableStatics;
            hasInheritableStatics = prototype.$hasInheritableStatics;
            if (!inheritableStatics) {
                inheritableStatics = prototype.$inheritableStatics = [];
                hasInheritableStatics = prototype.$hasInheritableStatics = {};
            }
            
            var className = Ext.getClassName(this) + '.';
            
            for (name in members) {
                if (members.hasOwnProperty(name)) {
                    member = members[name];
                    
                    if (typeof member == 'function') {
                        member.name = className + name;
                    }
                    
                    this[name] = member;
                    if (!hasInheritableStatics[name]) {
                        hasInheritableStatics[name] = true;
                        inheritableStatics.push(name);
                    }
                }
            }
            return this;
        },
        
        addMembers: function(members, isStatic, privacy) {
            var me = this,
                
                cloneFunction = Ext.Function.clone,
                target = isStatic ? me : me.prototype,
                defaultConfig = !isStatic && target.defaultConfig,
                enumerables = Ext.enumerables,
                privates = members.privates,
                configs, i, ln, member, name, subPrivacy, privateStatics;
            
            var displayName = (me.$className || '') + '#';
            
            if (privates) {
                
                
                delete members.privates;
                if (!isStatic) {
                    privateStatics = privates.statics;
                    delete privates.statics;
                }
                
                subPrivacy = privates.privacy || privacy || 'framework';
                
                me.addMembers(privates, isStatic, subPrivacy);
                if (privateStatics) {
                    me.addMembers(privateStatics, true, subPrivacy);
                }
            }
            for (name in members) {
                if (members.hasOwnProperty(name)) {
                    member = members[name];
                    
                    if (privacy === true) {
                        privacy = 'framework';
                    }
                    if (member && member.$nullFn && privacy !== member.$privacy) {
                        Ext.raise('Cannot use stock function for private method ' + (me.$className ? me.$className + '#' : '') + name);
                    }
                    
                    if (typeof member === 'function' && !member.$isClass && !member.$nullFn) {
                        if (member.$owner) {
                            member = cloneFunction(member);
                        }
                        if (target.hasOwnProperty(name)) {
                            member.$previous = target[name];
                        }
                        
                        
                        member.$owner = me;
                        member.$name = name;
                        
                        member.name = displayName + name;
                        var existing = target[name];
                        if (privacy) {
                            member.$privacy = privacy;
                            
                            
                            
                            
                            
                            
                            if (existing && existing.$privacy && existing.$privacy !== privacy) {
                                Ext.privacyViolation(me, existing, member, isStatic);
                            }
                        } else if (existing && existing.$privacy) {
                            Ext.privacyViolation(me, existing, member, isStatic);
                        }
                    }
                    
                    
                    
                    else if (defaultConfig && (name in defaultConfig) && !target.config.hasOwnProperty(name)) {
                        
                        
                        (configs || (configs = {}))[name] = member;
                        
                        continue;
                    }
                    target[name] = member;
                }
            }
            if (configs) {
                
                me.addConfig(configs);
            }
            if (enumerables) {
                for (i = 0 , ln = enumerables.length; i < ln; ++i) {
                    if (members.hasOwnProperty(name = enumerables[i])) {
                        member = members[name];
                        
                        if (member && !member.$nullFn) {
                            if (member.$owner) {
                                member = cloneFunction(member);
                            }
                            member.$owner = me;
                            member.$name = name;
                            
                            member.name = displayName + name;
                            
                            if (target.hasOwnProperty(name)) {
                                member.$previous = target[name];
                            }
                        }
                        target[name] = member;
                    }
                }
            }
            return this;
        },
        
        addMember: function(name, member) {
            oneMember[name] = member;
            this.addMembers(oneMember);
            delete oneMember[name];
            return this;
        },
        
        borrow: function(fromClass, members) {
            
            Ext.classSystemMonitor && Ext.classSystemMonitor(this, 'Ext.Base#borrow', arguments);
            
            var prototype = fromClass.prototype,
                membersObj = {},
                i, ln, name;
            members = Ext.Array.from(members);
            for (i = 0 , ln = members.length; i < ln; i++) {
                name = members[i];
                membersObj[name] = prototype[name];
            }
            return this.addMembers(membersObj);
        },
        
        override: function(members) {
            var me = this,
                statics = members.statics,
                inheritableStatics = members.inheritableStatics,
                config = members.config,
                mixins = members.mixins,
                cachedConfig = members.cachedConfig;
            if (statics || inheritableStatics || config) {
                members = Ext.apply({}, members);
            }
            if (statics) {
                me.addMembers(statics, true);
                delete members.statics;
            }
            if (inheritableStatics) {
                me.addInheritableStatics(inheritableStatics);
                delete members.inheritableStatics;
            }
            if (config) {
                me.addConfig(config);
                delete members.config;
            }
            if (cachedConfig) {
                me.addCachedConfig(cachedConfig);
                delete members.cachedConfig;
            }
            delete members.mixins;
            me.addMembers(members);
            if (mixins) {
                me.mixin(mixins);
            }
            return me;
        },
        
        callParent: function(args) {
            var method;
            
            return (method = this.callParent.caller) && (method.$previous || ((method = method.$owner ? method : method.caller) && method.$owner.superclass.self[method.$name])).apply(this, args || noArgs);
        },
        
        callSuper: function(args) {
            var method;
            
            return (method = this.callSuper.caller) && ((method = method.$owner ? method : method.caller) && method.$owner.superclass.self[method.$name]).apply(this, args || noArgs);
        },
        
        
        mixin: function(name, mixinClass) {
            var me = this,
                mixin, prototype, key, statics, i, ln, staticName, mixinValue, mixins;
            if (typeof name !== 'string') {
                mixins = name;
                if (mixins instanceof Array) {
                    for (i = 0 , ln = mixins.length; i < ln; i++) {
                        mixin = mixins[i];
                        me.mixin(mixin.prototype.mixinId || mixin.$className, mixin);
                    }
                } else {
                    
                    
                    
                    
                    for (var mixinName in mixins) {
                        me.mixin(mixinName, mixins[mixinName]);
                    }
                }
                return;
            }
            mixin = mixinClass.prototype;
            prototype = me.prototype;
            if (mixin.onClassMixedIn) {
                mixin.onClassMixedIn.call(mixinClass, me);
            }
            if (!prototype.hasOwnProperty('mixins')) {
                if ('mixins' in prototype) {
                    prototype.mixins = Ext.Object.chain(prototype.mixins);
                } else {
                    prototype.mixins = {};
                }
            }
            for (key in mixin) {
                mixinValue = mixin[key];
                if (key === 'mixins') {
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    Ext.applyIf(prototype.mixins, mixinValue);
                } else if (!(key === 'mixinId' || key === 'config') && (prototype[key] === undefined)) {
                    prototype[key] = mixinValue;
                }
            }
            
            
            statics = mixin.$inheritableStatics;
            if (statics) {
                for (i = 0 , ln = statics.length; i < ln; i++) {
                    staticName = statics[i];
                    if (!me.hasOwnProperty(staticName)) {
                        me[staticName] = mixinClass[staticName];
                    }
                }
            }
            
            
            if ('config' in mixin) {
                me.addConfig(mixin.config, mixinClass);
            }
            
            prototype.mixins[name] = mixin;
            if (mixin.afterClassMixedIn) {
                mixin.afterClassMixedIn.call(mixinClass, me);
            }
            return me;
        },
        
        
        
        addConfig: function(config, mixinClass) {
            var cfg = this.$config || this.getConfigurator();
            cfg.add(config, mixinClass);
        },
        addCachedConfig: function(config, isMixin) {
            var cached = {},
                key;
            for (key in config) {
                cached[key] = {
                    cached: true,
                    $value: config[key]
                };
            }
            this.addConfig(cached, isMixin);
        },
        
        getConfigurator: function() {
            
            return this.$config || new Ext.Configurator(this);
        },
        
        
        getName: function() {
            return Ext.getClassName(this);
        },
        
        createAlias: flexSetter(function(alias, origin) {
            aliasOneMember[alias] = function() {
                return this[origin].apply(this, arguments);
            };
            this.override(aliasOneMember);
            delete aliasOneMember[alias];
        })
    });
    
    
    for (baseStaticMember in Base) {
        if (Base.hasOwnProperty(baseStaticMember)) {
            baseStaticMembers.push(baseStaticMember);
        }
    }
    Base.$staticMembers = baseStaticMembers;
    
    Base.getConfigurator();
    
    
    Base.addMembers({
        
        $className: 'Ext.Base',
        
        isInstance: true,
        
        $configPrefixed: true,
        
        $configStrict: true,
        
        isConfiguring: false,
        
        isFirstInstance: false,
        
        destroyed: false,
        
        statics: function() {
            var method = this.statics.caller,
                self = this.self;
            if (!method) {
                return self;
            }
            return method.$owner;
        },
        
        callParent: function(args) {
            
            
            
            
            var method,
                superMethod = (method = this.callParent.caller) && (method.$previous || ((method = method.$owner ? method : method.caller) && method.$owner.superclass[method.$name]));
            
            if (!superMethod) {
                method = this.callParent.caller;
                var parentClass, methodName;
                if (!method.$owner) {
                    if (!method.caller) {
                        throw new Error("Attempting to call a protected method from the public scope, which is not allowed");
                    }
                    method = method.caller;
                }
                parentClass = method.$owner.superclass;
                methodName = method.$name;
                if (!(methodName in parentClass)) {
                    throw new Error("this.callParent() was called but there's no such method (" + methodName + ") found in the parent class (" + (Ext.getClassName(parentClass) || 'Object') + ")");
                }
            }
            
            return superMethod.apply(this, args || noArgs);
        },
        
        callSuper: function(args) {
            
            
            
            
            var method,
                superMethod = (method = this.callSuper.caller) && ((method = method.$owner ? method : method.caller) && method.$owner.superclass[method.$name]);
            
            if (!superMethod) {
                method = this.callSuper.caller;
                var parentClass, methodName;
                if (!method.$owner) {
                    if (!method.caller) {
                        throw new Error("Attempting to call a protected method from the public scope, which is not allowed");
                    }
                    method = method.caller;
                }
                parentClass = method.$owner.superclass;
                methodName = method.$name;
                if (!(methodName in parentClass)) {
                    throw new Error("this.callSuper() was called but there's no such method (" + methodName + ") found in the parent class (" + (Ext.getClassName(parentClass) || 'Object') + ")");
                }
            }
            
            return superMethod.apply(this, args || noArgs);
        },
        
        self: Base,
        
        constructor: function() {
            return this;
        },
        
        getConfigurator: function() {
            return this.$config || this.self.getConfigurator();
        },
        
        initConfig: function(instanceConfig) {
            var me = this,
                cfg = me.getConfigurator();
            me.initConfig = Ext.emptyFn;
            
            me.initialConfig = instanceConfig || {};
            cfg.configure(me, instanceConfig);
            return me;
        },
        beforeInitConfig: Ext.emptyFn,
        
        getConfig: getConfig,
        
        setConfig: function(name, value, 
        options) {
            
            
            
            
            
            var me = this,
                config;
            if (name) {
                if (typeof name === 'string') {
                    config = {};
                    config[name] = value;
                } else {
                    config = name;
                }
                me.getConfigurator().reconfigure(me, config, options);
            }
            return me;
        },
        
        getCurrentConfig: function() {
            var cfg = this.getConfigurator();
            return cfg.getCurrentConfig(this);
        },
        
        hasConfig: function(name) {
            return name in this.defaultConfig;
        },
        
        getInitialConfig: function(name) {
            var config = this.config;
            if (!name) {
                return config;
            }
            return config[name];
        },
        
        $links: null,
        
        link: function(name, value) {
            var me = this,
                links = me.$links || (me.$links = {});
            links[name] = true;
            me[name] = value;
            return value;
        },
        
        unlink: function(names) {
            var me = this,
                i, ln, link, value;
            
            if (!Ext.isArray(names)) {
                Ext.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;
        },
        
        destroy: function() {
            var me = this,
                links = me.$links;
            me.initialConfig = me.config = null;
            me.destroy = Ext.emptyFn;
            
            me.isDestroyed = me.destroyed = true;
            if (links) {
                me.$links = null;
                me.unlink(Ext.Object.getKeys(links));
            }
        }
    });
    
    BasePrototype.callOverridden = BasePrototype.callParent;
    
    Ext.privacyViolation = function(cls, existing, member, isStatic) {
        var name = member.$name,
            conflictCls = existing.$owner && existing.$owner.$className,
            s = isStatic ? 'static ' : '',
            msg = member.$privacy ? 'Private ' + s + member.$privacy + ' method "' + name + '"' : 'Public ' + s + 'method "' + name + '"';
        if (cls.$className) {
            msg = cls.$className + ': ' + msg;
        }
        if (!existing.$privacy) {
            msg += conflictCls ? ' hides public method inherited from ' + conflictCls : ' hides inherited public method.';
        } else {
            msg += conflictCls ? ' conflicts with private ' + existing.$privacy + ' method declared by ' + conflictCls : ' conflicts with inherited private ' + existing.$privacy + ' method.';
        }
        var compat = Ext.getCompatVersion();
        var ver = Ext.getVersion();
        
        if (ver && compat && compat.lt(ver)) {
            Ext.log.error(msg);
        } else {
            Ext.raise(msg);
        }
    };
    
    return Base;
}(Ext.Function.flexSetter));


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


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





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



Ext.ClassManager = (function(Class, alias, arraySlice, arrayFrom, global) {
    
    
    
    
    
    var makeCtor = Ext.Class.makeCtor,
        nameLookupStack = [],
        namespaceCache = {
            Ext: {
                name: 'Ext',
                value: Ext
            }
        },
        
        
        Manager = Ext.apply(new Ext.Inventory(), {
            
            classes: {},
            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);
                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,
                    
                    depedenciesLoaded,
                    classReady = function() {
                        var cls, dependencies, i, key, temp;
                        if (!depedenciesLoaded) {
                            dependencies = requires ? requires.slice(0) : [];
                            if (mixins) {
                                if (!(mixinsIsArray = mixins instanceof Array)) {
                                    for (key in mixins) {
                                        if (Ext.isString(cls = mixins[key])) {
                                            dependencies.push(cls);
                                        }
                                    }
                                } else {
                                    for (i = 0 , temp = mixins.length; i < temp; ++i) {
                                        if (Ext.isString(cls = mixins[i])) {
                                            dependencies.push(cls);
                                        }
                                    }
                                }
                            }
                            depedenciesLoaded = true;
                            if (dependencies.length) {
                                
                                
                                
                                Ext.require(dependencies, classReady);
                                return;
                            }
                        }
                        
                        
                        
                        
                        if (mixinsIsArray) {
                            for (i = 0 , temp = mixins.length; i < temp; ++i) {
                                if (Ext.isString(cls = mixins[i])) {
                                    mixins[i] = Ext.ClassManager.get(cls);
                                }
                            }
                        } else if (mixins) {
                            for (key in mixins) {
                                if (Ext.isString(cls = mixins[key])) {
                                    mixins[key] = Ext.ClassManager.get(cls);
                                }
                            }
                        }
                        
                        
                        cls = me.get(overriddenClassName);
                        
                        delete data.override;
                        delete data.compatibility;
                        delete data.requires;
                        delete data.uses;
                        Ext.override(cls, data);
                        
                        
                        
                        Ext.Loader.history.push(className);
                        if (uses) {
                            
                            
                            Ext['Loader'].addUsedClasses(uses);
                        }
                        
                        if (createdFn) {
                            createdFn.call(cls, cls);
                        }
                    };
                
                Manager.overrideMap[className] = true;
                
                
                
                if ('compatibility' in data && Ext.isString(compat = data.compatibility)) {
                    compat = Ext.checkVersion(compat);
                }
                if (compat) {
                    
                    me.onCreated(classReady, me, overriddenClassName);
                }
                me.triggerCreated(className, 2);
                return me;
            },
            
            instantiateByAlias: function() {
                var alias = arguments[0],
                    args = arraySlice.call(arguments),
                    className = this.getNameByAlias(alias);
                
                if (!className) {
                    throw new Error("[Ext.createByAlias] Unrecognized alias: " + alias);
                }
                
                args[0] = className;
                return Ext.create.apply(Ext, args);
            },
            
            dynInstantiate: function(name, args) {
                args = arrayFrom(args, true);
                args.unshift(name);
                return Ext.create.apply(Ext, args);
            },
            
            getInstantiator: function(length) {
                var instantiators = this.instantiators,
                    instantiator, i, args;
                instantiator = instantiators[length];
                if (!instantiator) {
                    i = length;
                    args = [];
                    for (i = 0; i < length; i++) {
                        args.push('a[' + i + ']');
                    }
                    instantiator = instantiators[length] = new Function('c', 'a', 'return new c(' + args.join(',') + ')');
                    
                    instantiator.name = "Ext.create" + length;
                }
                
                return instantiator;
            },
            
            postprocessors: {},
            
            defaultPostprocessors: [],
            
            registerPostprocessor: function(name, fn, properties, position, relativeTo) {
                if (!position) {
                    position = 'last';
                }
                if (!properties) {
                    properties = [
                        name
                    ];
                }
                this.postprocessors[name] = {
                    name: name,
                    properties: properties || false,
                    fn: fn
                };
                this.setDefaultPostprocessorPosition(name, position, relativeTo);
                return this;
            },
            
            setDefaultPostprocessors: function(postprocessors) {
                this.defaultPostprocessors = arrayFrom(postprocessors);
                return this;
            },
            
            setDefaultPostprocessorPosition: function(name, offset, relativeName) {
                var defaultPostprocessors = this.defaultPostprocessors,
                    index;
                if (typeof offset === 'string') {
                    if (offset === 'first') {
                        defaultPostprocessors.unshift(name);
                        return this;
                    } else if (offset === 'last') {
                        defaultPostprocessors.push(name);
                        return this;
                    }
                    offset = (offset === 'after') ? 1 : -1;
                }
                index = Ext.Array.indexOf(defaultPostprocessors, relativeName);
                if (index !== -1) {
                    Ext.Array.splice(defaultPostprocessors, Math.max(0, index + offset), 0, name);
                }
                return this;
            }
        });
    
    
    
    
    Manager.registerPostprocessor('alias', function(name, cls, data) {
        
        Ext.classSystemMonitor && Ext.classSystemMonitor(name, 'Ext.ClassManager#aliasPostProcessor', arguments);
        
        var aliases = Ext.Array.from(data.alias),
            i, ln;
        for (i = 0 , ln = aliases.length; i < ln; i++) {
            alias = aliases[i];
            this.addAlias(cls, alias);
        }
    }, [
        'xtype',
        'alias'
    ]);
    
    
    
    Manager.registerPostprocessor('singleton', function(name, cls, data, fn) {
        
        Ext.classSystemMonitor && Ext.classSystemMonitor(name, 'Ext.ClassManager#singletonPostProcessor', arguments);
        
        if (data.singleton) {
            fn.call(this, name, new cls(), data);
        } else {
            return true;
        }
        return false;
    });
    
    
    
    Manager.registerPostprocessor('alternateClassName', function(name, cls, data) {
        
        Ext.classSystemMonitor && Ext.classSystemMonitor(name, 'Ext.ClassManager#alternateClassNamePostprocessor', arguments);
        
        var alternates = data.alternateClassName,
            i, ln, alternate;
        if (!(alternates instanceof Array)) {
            alternates = [
                alternates
            ];
        }
        for (i = 0 , ln = alternates.length; i < ln; i++) {
            alternate = alternates[i];
            
            if (typeof alternate !== 'string') {
                throw new Error("[Ext.define] Invalid alternate of: '" + alternate + "' for class: '" + name + "'; must be a valid string");
            }
            
            this.set(alternate, cls);
        }
    });
    
    
    Manager.registerPostprocessor('debugHooks', function(name, Class, data) {
        
        Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#debugHooks', arguments);
        if (Ext.isDebugEnabled(Class.$className, data.debugHooks.$enabled)) {
            delete data.debugHooks.$enabled;
            Ext.override(Class, data.debugHooks);
        }
        
        
        var target = Class.isInstance ? Class.self : Class;
        delete target.prototype.debugHooks;
    });
    
    Manager.registerPostprocessor('deprecated', function(name, Class, data) {
        
        Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#deprecated', arguments);
        
        
        var target = Class.isInstance ? Class.self : Class;
        target.addDeprecations(data.deprecated);
        delete target.prototype.deprecated;
    });
    Ext.apply(Ext, {
        
        create: function() {
            var name = arguments[0],
                nameType = typeof name,
                args = arraySlice.call(arguments, 1),
                cls;
            if (nameType === 'function') {
                cls = name;
            } else {
                if (nameType !== 'string' && args.length === 0) {
                    args = [
                        name
                    ];
                    if (!(name = name.xclass)) {
                        name = args[0].xtype;
                        if (name) {
                            name = 'widget.' + name;
                        }
                    }
                }
                
                if (typeof name !== 'string' || name.length < 1) {
                    throw new Error("[Ext.create] Invalid class name or alias '" + name + "' specified, must be a non-empty string");
                }
                
                name = Manager.resolveName(name);
                cls = Manager.get(name);
            }
            
            if (!cls) {
                
                Ext.log.warn("[Ext.Loader] Synchronously loading '" + name + "'; consider adding " + "Ext.require('" + name + "') above Ext.onReady");
                
                Ext.syncRequire(name);
                cls = Manager.get(name);
            }
            
            if (!cls) {
                throw new Error("[Ext.create] Unrecognized class name / alias: " + name);
            }
            if (typeof cls !== 'function') {
                throw new Error("[Ext.create] Singleton '" + name + "' cannot be instantiated.");
            }
            
            return Manager.getInstantiator(args.length)(cls, args);
        },
        
        widget: function(name, config) {
            
            
            
            
            
            
            
            var xtype = name,
                alias, className, T;
            if (typeof xtype !== 'string') {
                
                
                config = name;
                
                xtype = config.xtype;
                className = config.xclass;
            } else {
                config = config || {};
            }
            if (config.isComponent) {
                return config;
            }
            if (!className) {
                alias = 'widget.' + xtype;
                className = Manager.getNameByAlias(alias);
            }
            
            if (className) {
                T = Manager.get(className);
            }
            if (!T) {
                return Ext.create(className || alias, config);
            }
            return new T(config);
        },
        
        createByAlias: alias(Manager, 'instantiateByAlias'),
        
        define: function(className, data, createdFn) {
            
            Ext.classSystemMonitor && Ext.classSystemMonitor(className, 'ClassManager#define', arguments);
            
            if (data.override) {
                Manager.classState[className] = 20;
                return Manager.createOverride.apply(Manager, arguments);
            }
            Manager.classState[className] = 10;
            return Manager.create.apply(Manager, arguments);
        },
        
        undefine: function(className) {
            
            Ext.classSystemMonitor && Ext.classSystemMonitor(className, 'Ext.ClassManager#undefine', arguments);
            
            var classes = Manager.classes;
            delete classes[className];
            delete Manager.existCache[className];
            delete Manager.classState[className];
            Manager.removeName(className);
            var entry = Manager.getNamespaceEntry(className),
                scope = entry.parent ? Manager.lookupName(entry.parent, false) : Ext.global;
            if (scope) {
                
                try {
                    delete scope[entry.name];
                } catch (e) {
                    scope[entry.name] = undefined;
                }
            }
        },
        
        getClassName: alias(Manager, 'getName'),
        
        getDisplayName: function(object) {
            if (object) {
                if (object.displayName) {
                    return object.displayName;
                }
                if (object.$name && object.$class) {
                    return Ext.getClassName(object.$class) + '#' + object.$name;
                }
                if (object.$className) {
                    return object.$className;
                }
            }
            return 'Anonymous';
        },
        
        getClass: alias(Manager, 'getClass'),
        
        namespace: 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.interceptAfter(data, 'onClassCreated', function() {
            
            Ext.classSystemMonitor && Ext.classSystemMonitor(cls, 'Ext.ClassManager#aliasPreprocessor#afterClassCreated', arguments);
            
            var mixins = prototype.mixins,
                key, mixin;
            for (key in mixins) {
                if (mixins.hasOwnProperty(key)) {
                    mixin = mixins[key];
                    xtypes = mixin.xtypes;
                    if (xtypes) {
                        for (i = 0 , ln = xtypes.length; i < ln; i++) {
                            xtype = xtypes[i];
                            if (!xtypesMap[xtype]) {
                                xtypesMap[xtype] = true;
                                xtypesChain.push(xtype);
                            }
                        }
                    }
                }
            }
        });
        for (i = 0 , ln = xtypes.length; i < ln; i++) {
            xtype = xtypes[i];
            
            if (typeof xtype !== 'string' || xtype.length < 1) {
                throw new Error("[Ext.define] Invalid xtype of: '" + xtype + "' for class: '" + name + "'; must be a valid non-empty string");
            }
            
            Ext.Array.include(aliases, widgetPrefix + xtype);
        }
        data.alias = aliases;
    }, [
        'xtype',
        'alias'
    ]);
    
    if (Ext.manifest) {
        var manifest = Ext.manifest,
            classes = manifest.classes,
            paths = manifest.paths,
            aliases = {},
            alternates = {},
            className, obj, name, path, baseUrl;
        if (paths) {
            
            
            
            if (manifest.bootRelative) {
                baseUrl = Ext.Boot.baseUrl;
                for (path in paths) {
                    if (paths.hasOwnProperty(path)) {
                        paths[path] = baseUrl + paths[path];
                    }
                }
            }
            Manager.setPath(paths);
        }
        if (classes) {
            for (className in classes) {
                alternates[className] = [];
                aliases[className] = [];
                obj = classes[className];
                if (obj.alias) {
                    aliases[className] = obj.alias;
                }
                if (obj.alternates) {
                    alternates[className] = obj.alternates;
                }
            }
        }
        Manager.addAlias(aliases);
        Manager.addAlternate(alternates);
    }
    return Manager;
}(Ext.Class, Ext.Function.alias, Array.prototype.slice, Ext.Array.from, Ext.global));


(Ext.env || (Ext.env = {})).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,
        i, prefix, mode, name, maxIEVersion;
    
    me.userAgent = userAgent;
    
    
    if (/Edge\//.test(userAgent)) {
        browserMatch = userAgent.match(/(Edge\/)([\w.]+)/);
    }
    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;
    }
    if (userAgent.match(/Android.*Chrome/g)) {
        browserName = 'ChromeMobile';
    }
    if (userAgent.match(/OPR/)) {
        browserName = 'Opera';
        browserMatch = userAgent.match(/OPR\/(\d+.\d+)/);
        browserVersion = new Ext.Version(browserMatch[1]);
    }
    Ext.apply(this, {
        engineName: engineName,
        engineVersion: engineVersion,
        name: browserName,
        version: browserVersion
    });
    this.setFlag(browserName, true, publish);
    
    if (browserVersion) {
        majorVer = browserVersion.getMajor() || '';
        
        if (me.is.IE) {
            majorVer = parseInt(majorVer, 10);
            mode = document.documentMode;
            
            
            
            
            
            if (mode === 7 || (majorVer === 7 && mode !== 8 && mode !== 9 && mode !== 10)) {
                majorVer = 7;
            } else if (mode === 8 || (majorVer === 8 && mode !== 8 && mode !== 9 && mode !== 10)) {
                majorVer = 8;
            } else if (mode === 9 || (majorVer === 9 && mode !== 7 && mode !== 8 && mode !== 10)) {
                majorVer = 9;
            } else if (mode === 10 || (majorVer === 10 && mode !== 7 && mode !== 8 && mode !== 9)) {
                majorVer = 10;
            } else if (mode === 11 || (majorVer === 11 && mode !== 7 && mode !== 8 && mode !== 9 && mode !== 10)) {
                majorVer = 11;
            }
            maxIEVersion = Math.max(majorVer, Ext.Boot.maxIEVersion);
            for (i = 7; i <= maxIEVersion; ++i) {
                prefix = 'isIE' + i;
                if (majorVer <= i) {
                    Ext[prefix + 'm'] = true;
                }
                if (majorVer === i) {
                    Ext[prefix] = true;
                }
                if (majorVer >= i) {
                    Ext[prefix + 'p'] = true;
                }
            }
        }
        if (me.is.Opera && parseInt(majorVer, 10) <= 12) {
            Ext.isOpera12m = true;
        }
        
        Ext.chromeVersion = Ext.isChrome ? majorVer : 0;
        Ext.firefoxVersion = Ext.isFirefox ? majorVer : 0;
        Ext.ieVersion = Ext.isIE ? majorVer : 0;
        Ext.operaVersion = Ext.isOpera ? majorVer : 0;
        Ext.safariVersion = Ext.isSafari ? majorVer : 0;
        Ext.webKitVersion = Ext.isWebKit ? majorVer : 0;
        this.setFlag(browserName + majorVer, true, publish);
        
        this.setFlag(browserName + browserVersion.getShortVersion());
    }
    for (i in browserNames) {
        if (browserNames.hasOwnProperty(i)) {
            name = browserNames[i];
            this.setFlag(name, browserName === name);
        }
    }
    this.setFlag(name);
    if (engineVersion) {
        this.setFlag(engineName + (engineVersion.getMajor() || ''));
        this.setFlag(engineName + engineVersion.getShortVersion());
    }
    for (i in engineNames) {
        if (engineNames.hasOwnProperty(i)) {
            name = engineNames[i];
            this.setFlag(name, engineName === name, publish);
        }
    }
    this.setFlag('Standalone', !!navigator.standalone);
    this.setFlag('Ripple', !!document.getElementById("tinyhippos-injected") && !Ext.isEmpty(window.top.ripple));
    this.setFlag('WebWorks', !!window.blackberry);
    if (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: {
        webkit: 'WebKit',
        gecko: 'Gecko',
        presto: 'Presto',
        trident: 'Trident',
        other: 'Other'
    },
    enginePrefixes: {
        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: ''
    },
    
    
    is: function(name) {
        return !!this.is[name];
    },
    
    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;
    },
    getPreferredTranslationMethod: function(config) {
        if (typeof config === 'object' && 'translationMethod' in config && config.translationMethod !== 'auto') {
            return config.translationMethod;
        } else {
            return 'csstransform';
        }
    }
};

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


Ext.env.OS = function(userAgent, platform, browserScope) {
    
    
    
    
    var me = this,
        names = 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 (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;
    var search = window.location.search.match(/deviceType=(Tablet|Phone)/),
        nativeDeviceType = window.deviceType;
    
    
    if (search && search[1]) {
        deviceType = search[1];
    } else if (nativeDeviceType === 'iPhone') {
        deviceType = 'Phone';
    } else if (nativeDeviceType === 'iPad') {
        deviceType = 'Tablet';
    } else {
        if (!osEnv.is.Android && !osEnv.is.iOS && !osEnv.is.WindowsPhone && /Windows|Linux|MacOS/.test(osName)) {
            deviceType = 'Desktop';
            
            Ext.browser.is.WebView = !!Ext.browser.is.Ripple;
        } else if (osEnv.is.iPad || osEnv.is.RIMTablet || osEnv.is.Android3 || Ext.browser.is.Silk || (osEnv.is.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: 'touchScroll',
            fn: function() {
                var touchScroll = 0;
                if (Ext.os.is.Desktop && (navigator.maxTouchPoints || navigator.msMaxTouchPoints)) {
                    touchScroll = 1;
                } else if (Ext.supports.Touch) {
                    touchScroll = 2;
                }
                return touchScroll;
            }
        },
        {
            
            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: 'TouchEvents',
            fn: function() {
                return this.isEventSupported('touchend');
            }
        },
        {
            name: 'PointerEvents',
            fn: function() {
                return navigator.pointerEnabled;
            }
        },
        {
            name: 'MSPointerEvents',
            fn: function() {
                return navigator.msPointerEnabled;
            }
        },
        {
            
            name: 'Orientation',
            fn: function() {
                return ('orientation' in window) && this.isEventSupported('orientationchange');
            }
        },
        {
            
            name: 'OrientationChange',
            fn: function() {
                return this.isEventSupported('orientationchange');
            }
        },
        {
            
            name: 'DeviceMotion',
            fn: function() {
                return this.isEventSupported('devicemotion');
            }
        },
        {
            
            
            names: [
                'Geolocation',
                'GeoLocation'
            ],
            fn: function() {
                return 'geolocation' in window.navigator;
            }
        },
        {
            name: 'SqlDatabase',
            fn: function() {
                return 'openDatabase' in window;
            }
        },
        {
            name: 'WebSockets',
            fn: function() {
                return 'WebSocket' in window;
            }
        },
        {
            
            name: 'Range',
            fn: function() {
                return !!document.createRange;
            }
        },
        {
            
            name: 'CreateContextualFragment',
            fn: function() {
                var range = !!document.createRange ? document.createRange() : false;
                return range && !!range.createContextualFragment;
            }
        },
        {
            
            name: 'History',
            fn: function() {
                return ('history' in window && 'pushState' in window.history);
            }
        },
        {
            
            name: '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: '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();
                    style = el.style;
                    style.height = '50px';
                    style.width = '50px';
                    style.overflow = 'auto';
                    style.position = 'absolute';
                    el.innerHTML = [
                        '<div style="display:table;height:100%;">',
                        
                        
                        
                        '<div style="width:51px;"></div>',
                        '</div>'
                    ].join('');
                    doc.body.appendChild(el);
                    if (el.firstChild.offsetHeight === 50) {
                        hasBug = true;
                    }
                    doc.body.removeChild(el);
                }
                return hasBug;
            }
        },
        
        {
            name: 'xOriginBug',
            ready: true,
            fn: function(doc, div) {
                div.innerHTML = '<div id="b1" style="height:100px;width:100px;direction:rtl;position:relative;overflow:scroll">' + '<div id="b2" style="position:relative;width:100%;height:20px;"></div>' + '<div id="b3" style="position:absolute;width:20px;height:20px;top:0px;right:0px"></div>' + '</div>';
                var outerBox = document.getElementById('b1').getBoundingClientRect(),
                    b2 = document.getElementById('b2').getBoundingClientRect(),
                    b3 = document.getElementById('b3').getBoundingClientRect();
                return (b2.left !== outerBox.left && b3.right !== outerBox.right);
            }
        },
        
        {
            name: 'ScrollWidthInlinePaddingBug',
            ready: true,
            fn: function(doc) {
                var hasBug = false,
                    style, el;
                el = doc.createElement('div');
                style = el.style;
                style.height = '50px';
                style.width = '50px';
                style.padding = '10px';
                style.overflow = 'hidden';
                style.position = 'absolute';
                el.innerHTML = '<span style="display:inline-block;zoom:1;height:60px;width:60px;"></span>';
                doc.body.appendChild(el);
                if (el.scrollWidth === 70) {
                    hasBug = true;
                }
                doc.body.removeChild(el);
                return hasBug;
            }
        },
        
        {
            name: 'rtlVertScrollbarOnRight',
            ready: true,
            fn: function(doc, div) {
                div.innerHTML = '<div style="height:100px;width:100px;direction:rtl;overflow:scroll">' + '<div style="width:20px;height:200px;"></div>' + '</div>';
                var outerBox = div.firstChild,
                    innerBox = outerBox.firstChild;
                return (innerBox.offsetLeft + innerBox.offsetWidth !== outerBox.offsetLeft + outerBox.offsetWidth);
            }
        },
        
        {
            name: 'rtlVertScrollbarOverflowBug',
            ready: true,
            fn: function(doc, div) {
                div.innerHTML = '<div style="height:100px;width:100px;direction:rtl;overflow:auto">' + '<div style="width:95px;height:200px;"></div>' + '</div>';
                
                
                
                var outerBox = div.firstChild;
                return outerBox.clientHeight === outerBox.offsetHeight;
            }
        },
        {
            identity: 'defineProperty',
            fn: function() {
                if (Ext.isIE8m) {
                    Ext.Object.defineProperty = Ext.emptyFn;
                    return false;
                }
                return true;
            }
        },
        {
            identify: 'nativeXhr',
            fn: function() {
                if (typeof XMLHttpRequest !== 'undefined') {
                    return true;
                }
                
                XMLHttpRequest = function() {
                    
                    try {
                        return new ActiveXObject('MSXML2.XMLHTTP.3.0');
                    } 
                    catch (ex) {
                        return null;
                    }
                };
                return false;
            }
        },
        
        {
            name: 'SpecialKeyDownRepeat',
            fn: function() {
                return Ext.isWebKit ? parseInt(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1], 10) >= 525 : !((Ext.isGecko && !Ext.isWindows) || (Ext.isOpera && Ext.operaVersion < 12));
            }
        },
        
        {
            name: 'EmulatedMouseOver',
            fn: function() {
                
                return Ext.os.is.iOS;
            }
        },
        
        {
            
            name: 'Hashchange',
            fn: function() {
                
                var docMode = document.documentMode;
                return 'onhashchange' in window && (docMode === undefined || docMode > 7);
            }
        },
        
        {
            name: 'FixedTableWidthBug',
            ready: true,
            fn: function() {
                if (Ext.isIE8) {
                    
                    return false;
                }
                var outer = document.createElement('div'),
                    inner = document.createElement('div'),
                    width;
                outer.setAttribute('style', 'display:table;table-layout:fixed;');
                inner.setAttribute('style', 'display:table-cell;min-width:50px;');
                outer.appendChild(inner);
                document.body.appendChild(outer);
                
                outer.offsetWidth;
                
                outer.style.width = '25px';
                width = outer.offsetWidth;
                document.body.removeChild(outer);
                return width === 50;
            }
        },
        
        {
            name: 'FocusinFocusoutEvents',
            fn: function() {
                
                
                
                
                return !Ext.isGecko;
            }
        },
        
        {
            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;
            }
        },
        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)) {
                    clearTimeout(Ready.scrollTimer);
                    Ready.scrollTimer = null;
                }
            },
            
            onReadyStateChange: function() {
                var state = document.readyState;
                if (Ready.readyStatesRe.test(state)) {
                    Ready.onReadyEvent({
                        type: state
                    });
                }
            }
        });
    }
    
    
    
    
    Ext.onDocumentReady = function(fn, scope, options) {
        var opt = {
                dom: true
            };
        if (options) {
            Ext.apply(opt, options);
        }
        Ready.on(fn, scope, opt);
    };
    
    Ext.onReady = function(fn, scope, options) {
        Ready.on(fn, scope, options);
    };
    
    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 = {},
        _missingQueue = {},
        _config = {
            
            enabled: true,
            
            scriptChainDelay: false,
            
            disableCaching: true,
            
            disableCachingParam: '_dc',
            
            paths: Manager.paths,
            
            preserveScripts: true,
            
            scriptCharset: undefined
        },
        
        delegatedConfigs = {
            disableCaching: true,
            disableCachingParam: true,
            preserveScripts: true,
            scriptChainDelay: 'loadDelay'
        };
    Ext.apply(Loader, {
        
        isInHistory: isInHistory,
        
        isLoading: false,
        
        history: history,
        
        config: _config,
        
        readyListeners: readyListeners,
        
        optionalRequires: usedClasses,
        
        requiresMap: _requiresMap,
        
        hasFileLoadError: false,
        
        scriptsLoading: 0,
        
        
        classesLoading: [],
        
        
        syncModeEnabled: false,
        
        missingQueue: _missingQueue,
        init: function() {
            
            var scripts = document.getElementsByTagName('script'),
                src = scripts[scripts.length - 1].src,
                path = src.substring(0, src.lastIndexOf('/') + 1),
                meta = Ext._classPathMetadata,
                microloader = Ext.Microloader,
                manifest = Ext.manifest,
                loadOrder, baseUrl, loadlen, l, loadItem;
            
            if (src.indexOf("packages/core/src/") !== -1) {
                path = path + "../../";
            } else if (src.indexOf("/core/src/class/") !== -1) {
                path = path + "../../../";
            }
            
            if (!Manager.getPath("Ext")) {
                Manager.setPath('Ext', path + 'src');
            }
            
            if (meta) {
                Ext._classPathMetadata = null;
                Loader.addClassPathMappings(meta);
            }
            if (manifest) {
                loadOrder = manifest.loadOrder;
                
                
                
                baseUrl = Ext.Boot.baseUrl;
                if (loadOrder && manifest.bootRelative) {
                    for (loadlen = loadOrder.length , l = 0; l < loadlen; l++) {
                        loadItem = loadOrder[l];
                        loadItem.path = baseUrl + loadItem.path;
                    }
                }
            }
            if (microloader) {
                Ready.block();
                microloader.onMicroloaderReady(function() {
                    Ready.unblock();
                });
            }
        },
        
        setConfig: Ext.Function.flexSetter(function(name, value) {
            if (name === 'paths') {
                Loader.setPath(value);
            } else {
                _config[name] = value;
                var delegated = delegatedConfigs[name];
                if (delegated) {
                    Boot.setConfig((delegated === true) ? name : delegated, value);
                }
            }
            return Loader;
        }),
        
        getConfig: function(name) {
            return name ? _config[name] : _config;
        },
        
        setPath: function() {
            
            Manager.setPath.apply(Manager, arguments);
            return Loader;
        },
        
        addClassPathMappings: function(paths) {
            
            Manager.setPath(paths);
            return Loader;
        },
        
        addBaseUrlClassPathMappings: function(pathConfig) {
            for (var name in pathConfig) {
                pathConfig[name] = Boot.baseUrl + pathConfig[name];
            }
            Ext.Loader.addClassPathMappings(pathConfig);
        },
        
        getPath: function(className) {
            
            return Manager.getPath(className);
        },
        require: function(expressions, fn, scope, excludes) {
            if (excludes) {
                return Loader.exclude(excludes).require(expressions, fn, scope);
            }
            var classNames = Manager.getNamesByExpression(expressions);
            return Loader.load(classNames, fn, scope);
        },
        syncRequire: function() {
            var wasEnabled = Loader.syncModeEnabled;
            Loader.syncModeEnabled = true;
            var ret = Loader.require.apply(Loader, arguments);
            Loader.syncModeEnabled = wasEnabled;
            return ret;
        },
        exclude: function(excludes) {
            var selector = Manager.select({
                    require: function(classNames, fn, scope) {
                        return Loader.load(classNames, fn, scope);
                    },
                    syncRequire: function(classNames, fn, scope) {
                        var wasEnabled = Loader.syncModeEnabled;
                        Loader.syncModeEnabled = true;
                        var ret = Loader.load(classNames, fn, scope);
                        Loader.syncModeEnabled = wasEnabled;
                        return ret;
                    }
                });
            selector.exclude(excludes);
            return selector;
        },
        load: function(classNames, callback, scope) {
            if (callback) {
                if (callback.length) {
                    
                    
                    callback = Loader.makeLoadCallback(classNames, callback);
                }
                callback = callback.bind(scope || Ext.global);
            }
            var missingClassNames = [],
                numClasses = classNames.length,
                className, i, numMissing,
                urls = [],
                state = Manager.classState;
            for (i = 0; i < numClasses; ++i) {
                className = Manager.resolveName(classNames[i]);
                if (!Manager.isCreated(className)) {
                    missingClassNames.push(className);
                    _missingQueue[className] = Loader.getPath(className);
                    if (!state[className]) {
                        urls.push(_missingQueue[className]);
                    }
                }
            }
            
            
            numMissing = missingClassNames.length;
            if (numMissing) {
                Loader.missingCount += numMissing;
                
                Ext.Array.push(Loader.classesLoading, missingClassNames);
                
                Manager.onCreated(function() {
                    
                    Ext.Array.remove(Loader.classesLoading, missingClassNames);
                    Ext.each(missingClassNames, function(name) {
                        Ext.Array.remove(Loader.classesLoading, name);
                    });
                    
                    if (callback) {
                        Ext.callback(callback, scope, arguments);
                    }
                    Loader.checkReady();
                }, Loader, missingClassNames);
                if (!_config.enabled) {
                    Ext.raise("Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " + "Missing required class" + ((missingClassNames.length > 1) ? "es" : "") + ": " + missingClassNames.join(', '));
                }
                if (urls.length) {
                    Loader.loadScripts({
                        url: urls,
                        
                        _classNames: missingClassNames
                    });
                } else {
                    
                    
                    
                    Loader.checkReady();
                }
            } else {
                if (callback) {
                    callback.call(scope);
                }
                
                
                
                Loader.checkReady();
            }
            if (Loader.syncModeEnabled) {
                
                if (numClasses === 1) {
                    return Manager.get(classNames[0]);
                }
            }
            return Loader;
        },
        makeLoadCallback: function(classNames, callback) {
            return function() {
                var classes = [],
                    i = classNames.length;
                while (i-- > 0) {
                    classes[i] = Manager.get(classNames[i]);
                }
                return callback.apply(this, classes);
            };
        },
        onLoadFailure: function() {
            var options = this,
                onError = options.onError;
            Loader.hasFileLoadError = true;
            --Loader.scriptsLoading;
            if (onError) {
                
                onError.call(options.userScope, options);
            } else 
            {
                Ext.log.error("[Ext.Loader] Some requested files failed to load.");
            }
            
            Loader.checkReady();
        },
        onLoadSuccess: function() {
            var options = this,
                onLoad = options.onLoad;
            --Loader.scriptsLoading;
            if (onLoad) {
                
                onLoad.call(options.userScope, options);
            }
            
            Loader.checkReady();
        },
        
        
        reportMissingClasses: function() {
            if (!Loader.syncModeEnabled && !Loader.scriptsLoading && Loader.isLoading && !Loader.hasFileLoadError) {
                var missingClasses = [],
                    missingPaths = [];
                for (var missingClassName in _missingQueue) {
                    missingClasses.push(missingClassName);
                    missingPaths.push(_missingQueue[missingClassName]);
                }
                if (missingClasses.length) {
                    throw new Error("The following classes are not declared even if their files have been " + "loaded: '" + missingClasses.join("', '") + "'. Please check the source code of their " + "corresponding files for possible typos: '" + missingPaths.join("', '"));
                }
            }
        },
        
        
        onReady: function(fn, scope, withDomReady, options) {
            if (withDomReady) {
                Ready.on(fn, scope, options);
            } else {
                var listener = Ready.makeListener(fn, scope, options);
                if (Loader.isLoading) {
                    readyListeners.push(listener);
                } else {
                    Ready.invoke(listener);
                }
            }
        },
        
        addUsedClasses: function(classes) {
            var cls, i, ln;
            if (classes) {
                classes = (typeof classes === 'string') ? [
                    classes
                ] : classes;
                for (i = 0 , ln = classes.length; i < ln; i++) {
                    cls = classes[i];
                    if (typeof cls === 'string' && !Ext.Array.contains(usedClasses, cls)) {
                        usedClasses.push(cls);
                    }
                }
            }
            return Loader;
        },
        
        triggerReady: function() {
            var listener,
                refClasses = usedClasses;
            if (Loader.isLoading && refClasses.length) {
                
                usedClasses = [];
                
                
                Loader.require(refClasses);
            } else {
                
                
                Loader.isLoading = false;
                
                
                readyListeners.sort(Ready.sortFn);
                
                
                
                while (readyListeners.length && !Loader.isLoading) {
                    
                    
                    listener = readyListeners.pop();
                    Ready.invoke(listener);
                }
                
                
                
                
                
                
                
                
                Ready.unblock();
            }
        },
        
        historyPush: function(className) {
            if (className && !isInHistory[className] && !Manager.overrideMap[className]) {
                isInHistory[className] = true;
                history.push(className);
            }
            return Loader;
        },
        
        loadScripts: function(params) {
            var manifest = Ext.manifest,
                loadOrder = manifest && manifest.loadOrder,
                loadOrderMap = manifest && manifest.loadOrderMap,
                options;
            ++Loader.scriptsLoading;
            
            
            if (loadOrder && !loadOrderMap) {
                manifest.loadOrderMap = loadOrderMap = Boot.createLoadOrderMap(loadOrder);
            }
            
            
            Loader.checkReady();
            options = Ext.apply({
                loadOrder: loadOrder,
                loadOrderMap: loadOrderMap,
                charset: _config.scriptCharset,
                success: Loader.onLoadSuccess,
                failure: Loader.onLoadFailure,
                sync: Loader.syncModeEnabled,
                _classNames: []
            }, params);
            options.userScope = options.scope;
            options.scope = options;
            Boot.load(options);
        },
        
        loadScriptsSync: function(urls) {
            var syncwas = Loader.syncModeEnabled;
            Loader.syncModeEnabled = true;
            Loader.loadScripts({
                url: urls
            });
            Loader.syncModeEnabled = syncwas;
        },
        
        loadScriptsSyncBasePrefix: function(urls) {
            var syncwas = Loader.syncModeEnabled;
            Loader.syncModeEnabled = true;
            Loader.loadScripts({
                url: urls,
                prependBaseUrl: true
            });
            Loader.syncModeEnabled = syncwas;
        },
        
        loadScript: function(options) {
            var isString = typeof options === 'string',
                isArray = options instanceof Array,
                isObject = !isArray && !isString,
                url = isObject ? options.url : options,
                onError = isObject && options.onError,
                onLoad = isObject && options.onLoad,
                scope = isObject && options.scope,
                request = {
                    url: url,
                    scope: scope,
                    onLoad: onLoad,
                    onError: onError,
                    _classNames: []
                };
            Loader.loadScripts(request);
        },
        
        flushMissingQueue: function() {
            var name, val,
                missingwas = 0,
                missing = 0;
            for (name in _missingQueue) {
                missingwas++;
                val = _missingQueue[name];
                if (Manager.isCreated(name)) {
                    delete _missingQueue[name];
                } else if (Manager.existCache[name] === 2) {
                    delete _missingQueue[name];
                } else {
                    ++missing;
                }
            }
            this.missingCount = missing;
        },
        
        checkReady: function() {
            var wasLoading = Loader.isLoading,
                isLoading;
            Loader.flushMissingQueue();
            isLoading = Loader.missingCount + Loader.scriptsLoading;
            if (isLoading && !wasLoading) {
                Ready.block();
                Loader.isLoading = !!isLoading;
            } else if (!isLoading && wasLoading) {
                Loader.triggerReady();
            }
            
            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 deadlockPath = [],
            detectDeadlock;
        
        if (className) {
            requiredMap = Loader.requiredByMap || (Loader.requiredByMap = {});
            for (i = 0 , ln = dependencies.length; i < ln; i++) {
                dependency = dependencies[i];
                (requiredMap[dependency] || (requiredMap[dependency] = [])).push(className);
            }
            detectDeadlock = function(cls) {
                deadlockPath.push(cls);
                if (_requiresMap[cls]) {
                    if (Ext.Array.contains(_requiresMap[cls], className)) {
                        Ext.raise("Circular requirement detected! '" + className + "' and '" + deadlockPath[1] + "' mutually require each other. Path: " + deadlockPath.join(' -> ') + " -> " + deadlockPath[0]);
                    }
                    for (i = 0 , ln = _requiresMap[cls].length; i < ln; i++) {
                        detectDeadlock(_requiresMap[cls][i]);
                    }
                }
            };
            detectDeadlock(className);
        }
        
        (className ? Loader.exclude(className) : Loader).require(dependencies, function() {
            for (i = 0 , ln = dependencyProperties.length; i < ln; i++) {
                propertyName = dependencyProperties[i];
                if (data.hasOwnProperty(propertyName)) {
                    propertyValue = data[propertyName];
                    if (typeof propertyValue === 'string') {
                        data[propertyName] = Manager.get(propertyValue);
                    } else if (propertyValue instanceof Array) {
                        for (j = 0 , subLn = propertyValue.length; j < subLn; j++) {
                            value = propertyValue[j];
                            if (typeof value === 'string') {
                                data[propertyName][j] = Manager.get(value);
                            }
                        }
                    } else if (typeof propertyValue !== 'function') {
                        for (var k in propertyValue) {
                            if (propertyValue.hasOwnProperty(k)) {
                                value = propertyValue[k];
                                if (typeof value === 'string') {
                                    data[propertyName][k] = Manager.get(value);
                                }
                            }
                        }
                    }
                }
            }
            continueFn.call(me, cls, data, hooks);
        });
        return false;
    }, true, 'after', 'className');
    
    Manager.registerPostprocessor('uses', function(name, cls, data) {
        
        Ext.classSystemMonitor && Ext.classSystemMonitor(cls, 'Ext.Loader#usesPostprocessor', arguments);
        
        
        var manifest = Ext.manifest,
            loadOrder = manifest && manifest.loadOrder,
            classes = manifest && manifest.classes,
            uses, clazz, item, len, i, indexMap;
        if (loadOrder) {
            clazz = classes[name];
            if (clazz && !isNaN(i = clazz.idx)) {
                item = loadOrder[i];
                uses = item.uses;
                indexMap = {};
                for (len = uses.length , i = 0; i < len; i++) {
                    indexMap[uses[i]] = true;
                }
                uses = Ext.Boot.getPathsFromIndexes(indexMap, loadOrder, true);
                if (uses.length > 0) {
                    Loader.loadScripts({
                        url: uses,
                        sequential: true
                    });
                }
            }
        }
        if (data.uses) {
            uses = data.uses;
            Loader.addUsedClasses(uses);
        }
    });
    Manager.onCreated(Loader.historyPush);
    
    Loader.init();
}());


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



Ext.define('Ext.util.Event', function() {
    var arraySlice = Array.prototype.slice,
        arrayInsert = Ext.Array.insert,
        toArray = Ext.Array.toArray,
        fireArgs = {};
    return {
        
        isEvent: true,
        
        suspended: 0,
        noOptions: {},
        constructor: function(observable, name) {
            this.name = name;
            this.observable = observable;
            this.listeners = [];
        },
        addListener: function(fn, scope, options, caller, manager) {
            var me = this,
                added = false,
                observable = me.observable,
                eventName = me.name,
                listeners, listener, priority, isNegativePriority, highestNegativePriorityIndex, hasNegativePriorityIndex, length, index, i, listenerPriority;
            
            if (scope && !Ext._namedScopes[scope] && (typeof fn === 'string') && (typeof scope[fn] !== 'function')) {
                Ext.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).subscribe(observable, eventName, options.delegated !== false, options.capture);
                }
                added = true;
            }
            return added;
        },
        createListener: function(fn, scope, o, caller, manager) {
            var me = this,
                namedScope = Ext._namedScopes[scope],
                listener = {
                    fn: fn,
                    scope: scope,
                    ev: me,
                    caller: caller,
                    manager: manager,
                    namedScope: namedScope,
                    defaultScope: namedScope ? (scope || me.observable) : undefined,
                    lateBound: typeof fn === 'string'
                },
                handler = fn,
                wrapped = false,
                type;
            
            
            if (o) {
                listener.o = o;
                if (o.single) {
                    handler = me.createSingle(handler, listener, o, scope);
                    wrapped = true;
                }
                if (o.target) {
                    handler = me.createTargeted(handler, listener, o, scope, wrapped);
                    wrapped = true;
                }
                if (o.delay) {
                    handler = me.createDelayed(handler, listener, o, scope, wrapped);
                    wrapped = true;
                }
                if (o.buffer) {
                    handler = me.createBuffered(handler, listener, o, scope, wrapped);
                    wrapped = true;
                }
                if (me.observable.isElement) {
                    
                    
                    
                    type = o.type;
                    if (type) {
                        listener.type = type;
                    }
                }
            }
            listener.fireFn = handler;
            listener.wrapped = wrapped;
            return listener;
        },
        findListener: function(fn, scope) {
            var listeners = this.listeners,
                i = listeners.length,
                listener;
            while (i--) {
                listener = listeners[i];
                if (listener) {
                    
                    if (listener.fn === fn && listener.scope == scope) {
                        return i;
                    }
                }
            }
            return -1;
        },
        removeListener: function(fn, scope, index) {
            var me = this,
                removed = false,
                observable = me.observable,
                eventName = me.name,
                listener, highestNegativePriorityIndex, options, k, manager, managedListeners, managedListener, i;
            index = index || me.findListener(fn, scope);
            if (index != -1) {
                listener = me.listeners[index];
                options = listener.o;
                highestNegativePriorityIndex = me._highestNegativePriorityIndex;
                if (me.firing) {
                    me.listeners = me.listeners.slice(0);
                }
                
                if (listener.task) {
                    listener.task.cancel();
                    delete listener.task;
                }
                
                k = listener.tasks && listener.tasks.length;
                if (k) {
                    while (k--) {
                        listener.tasks[k].cancel();
                    }
                    delete listener.tasks;
                }
                
                
                
                me.listeners.splice(index, 1);
                manager = listener.manager;
                if (manager) {
                    
                    
                    
                    
                    
                    
                    
                    
                    managedListeners = manager.managedListeners;
                    if (managedListeners) {
                        for (i = managedListeners.length; i--; ) {
                            managedListener = managedListeners[i];
                            if (managedListener.item === me.observable && managedListener.ename === eventName && managedListener.fn === fn && managedListener.scope === scope) {
                                managedListeners.splice(i, 1);
                            }
                        }
                    }
                }
                
                
                if (highestNegativePriorityIndex) {
                    if (index < highestNegativePriorityIndex) {
                        me._highestNegativePriorityIndex--;
                    } else if (index === highestNegativePriorityIndex && index === me.listeners.length) {
                        delete me._highestNegativePriorityIndex;
                    }
                }
                if (observable.isElement) {
                    observable._getPublisher(eventName).unsubscribe(observable, eventName, options.delegated !== false, options.capture);
                }
                removed = true;
            }
            return removed;
        },
        
        clearListeners: function() {
            var listeners = this.listeners,
                i = listeners.length,
                listener;
            while (i--) {
                listener = listeners[i];
                this.removeListener(listener.fn, listener.scope);
            }
        },
        suspend: function() {
            ++this.suspended;
        },
        resume: function() {
            if (this.suspended) {
                --this.suspended;
            }
        },
        isSuspended: function() {
            return this.suspended > 0;
        },
        fireDelegated: function(firingObservable, args) {
            this.firingObservable = firingObservable;
            return this.fire.apply(this, args);
        },
        fire: function() {
            var me = this,
                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];
                    options = listener.o;
                    if (isElement) {
                        if (currentTarget) {
                            
                            
                            e.setCurrentTarget(currentTarget);
                        }
                        
                        
                        
                        
                        type = listener.type;
                        if (type) {
                            
                            
                            
                            
                            chained = e;
                            e = args[0] = chained.chain({
                                type: type
                            });
                        }
                        
                        
                        Ext.EventObject = e;
                    }
                    firingArgs = args;
                    if (options) {
                        delegate = options.delegate;
                        if (delegate) {
                            if (isElement) {
                                
                                
                                delegateEl = e.getTarget('#' + e.currentTarget.id + ' ' + delegate);
                                if (delegateEl) {
                                    args[1] = delegateEl;
                                    
                                    
                                    currentTarget = e.currentTarget;
                                    e.setCurrentTarget(delegateEl);
                                } else {
                                    
                                    continue;
                                }
                            } else if (isComponent && !firingObservable.is('#' + observable.id + ' ' + options.delegate)) {
                                
                                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 (fireFn.apply(fireScope, firingArgs) === false) {
                        Ext.EventObject = null;
                        return (me.firing = false);
                    }
                    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;
        },
        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() {
                var fireInfo;
                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);
                task.delay(o.delay || 10, handler, scope, toArray(arguments));
            };
        },
        createSingle: function(handler, listener, o, scope, wrapped) {
            return function() {
                var event = listener.ev,
                    fireInfo;
                if (event.removeListener(listener.fn, scope) && event.observable) {
                    
                    
                    event.observable.hasListeners[event.name]--;
                }
                if (!wrapped) {
                    fireInfo = event.getFireInfo(listener, true);
                    handler = fireInfo.fn;
                    scope = fireInfo.scope;
                    
                    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);
        };
    ListenerRemover.prototype.destroy = function() {
        this.destroy = Ext.emptyFn;
        var observable = this.observable;
        observable[this.managedListeners ? 'mun' : 'un'].apply(observable, this.args);
    };
    return {
        extend: Ext.Mixin,
        mixinConfig: {
            id: 'observable',
            after: {
                destroy: 'clearListeners'
            }
        },
        mixins: [
            Ext.mixin.Identifiable
        ],
        statics: {
            
            releaseCapture: function(o) {
                o.fireEventArgs = this.prototype.fireEventArgs;
            },
            
            capture: function(o, fn, scope) {
                
                
                
                
                var newFn = function(eventName, args) {
                        return fn.apply(scope, [
                            eventName
                        ].concat(args));
                    };
                this.captureArgs(o, newFn, scope);
            },
            
            captureArgs: function(o, fn, scope) {
                o.fireEventArgs = Ext.Function.createInterceptor(o.fireEventArgs, fn, scope);
            },
            
            observe: function(cls, listeners) {
                if (cls) {
                    if (!cls.isObservable) {
                        Ext.applyIf(cls, new this());
                        this.captureArgs(cls.prototype, cls.fireEventArgs, cls);
                    }
                    if (Ext.isObject(listeners)) {
                        cls.on(listeners);
                    }
                }
                return cls;
            },
            
            prepareClass: function(T, mixin, data) {
                
                
                
                
                var listeners = T.listeners = [],
                    
                    
                    
                    
                    target = data || T.prototype,
                    targetListeners = target.listeners,
                    superListeners = mixin ? mixin.listeners : T.superclass.self.listeners,
                    name, scope, namedScope;
                
                
                
                
                if (superListeners) {
                    listeners.push(superListeners);
                }
                if (targetListeners) {
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    scope = targetListeners.scope;
                    if (!scope) {
                        targetListeners.scope = 'self';
                    } else {
                        namedScope = Ext._namedScopes[scope];
                        if (namedScope && namedScope.isController) {
                            targetListeners.scope = 'self.controller';
                        }
                    }
                    listeners.push(targetListeners);
                    
                    
                    
                    
                    
                    target.listeners = null;
                }
                if (!T.HasListeners) {
                    
                    
                    
                    var HasListeners = function() {},
                        SuperHL = T.superclass.HasListeners || (mixin && mixin.HasListeners) || Observable.HasListeners;
                    
                    T.prototype.HasListeners = T.HasListeners = HasListeners;
                    
                    
                    HasListeners.prototype = T.hasListeners = new SuperHL();
                }
            }
        },
        
        
        
        isObservable: true,
        
        eventsSuspended: 0,
        
        constructor: function(config) {
            var me = this,
                self = me.self,
                declaredListeners, listeners, bubbleEvents, len, i;
            
            
            
            
            if (me.$observableInitialized) {
                return;
            }
            me.$observableInitialized = true;
            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 (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(ename, fn, scope, options, order, 
        caller) {
            var me = this,
                namedScopes = Ext._namedScopes,
                config, namedScope, isClassListener, innerScope, eventOptions;
            
            if (typeof ename !== 'string') {
                options = ename;
                scope = options.scope;
                namedScope = scope && namedScopes[scope];
                isClassListener = namedScope && namedScope.isSelf;
                
                
                eventOptions = ((me.isComponent || me.isWidget) && options.element) ? me.$elementEventOptions : me.$eventOptions;
                for (ename in options) {
                    config = options[ename];
                    if (!eventOptions[ename]) {
                        
                        innerScope = config.scope;
                        
                        
                        
                        
                        
                        if (innerScope && isClassListener) {
                            namedScope = namedScopes[innerScope];
                            if (namedScope && namedScope.isController) {
                                innerScope = 'self.controller';
                            }
                        }
                        me.doAddListener(ename, config.fn || config, innerScope || scope, config.fn ? config : options, order, caller);
                    }
                }
                if (options && options.destroyable) {
                    return new ListenerRemover(me, options);
                }
            } else {
                me.doAddListener(ename, fn, scope, options, order, caller);
                if (options && options.destroyable) {
                    return new ListenerRemover(me, ename, fn, scope, options);
                }
            }
            return me;
        },
        
        removeListener: function(ename, fn, scope, 
        eventOptions) {
            var me = this,
                config, options;
            if (typeof ename !== 'string') {
                options = ename;
                
                
                eventOptions = eventOptions || me.$eventOptions;
                for (ename in options) {
                    if (options.hasOwnProperty(ename)) {
                        config = options[ename];
                        if (!me.$eventOptions[ename]) {
                            me.doRemoveListener(ename, config.fn || config, config.scope || options.scope);
                        }
                    }
                }
            } else {
                me.doRemoveListener(ename, fn, scope);
            }
            return me;
        },
        
        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 ? me.managedListeners.slice() : [],
                i = 0,
                len = managedListeners.length;
            for (; i < len; i++) {
                me.removeManagedListenerItem(true, managedListeners[i]);
            }
            me.managedListeners = [];
        },
        
        removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope) {
            if (isClear || (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope))) {
                
                managedListener.item.doRemoveListener(managedListener.ename, managedListener.fn, managedListener.scope, managedListener.options);
                if (!isClear) {
                    Ext.Array.remove(this.managedListeners, managedListener);
                }
            }
        },
        
        purgeManagedListeners: function() {
            if (Ext.global.console) {
                Ext.global.console.warn('Observable: purgeManagedListeners has been deprecated. Please use clearManagedListeners.');
            }
            return this.clearManagedListeners.apply(this, arguments);
        },
        
        
        hasListener: function(ename) {
            ename = Ext.canonicalEventName(ename);
            return !!this.hasListeners[ename];
        },
        
        isSuspended: function(event) {
            var suspended = this.eventsSuspended > 0,
                events = this.events;
            if (!suspended && event && events) {
                event = events[event];
                if (event && event.isEvent) {
                    return event.isSuspended();
                }
            }
            return suspended;
        },
        
        suspendEvents: function(queueSuspended) {
            ++this.eventsSuspended;
            if (queueSuspended && !this.eventQueue) {
                this.eventQueue = [];
            }
        },
        
        suspendEvent: function() {
            var me = this,
                events = me.events,
                len = arguments.length,
                i, event, ename;
            for (i = 0; i < len; i++) {
                ename = arguments[i];
                ename = Ext.canonicalEventName(ename);
                event = events[ename];
                
                if (!event || !event.isEvent) {
                    event = me._initEvent(ename);
                }
                event.suspend();
            }
        },
        
        resumeEvent: function() {
            var events = this.events || 0,
                len = events && arguments.length,
                i, event;
            for (i = 0; i < len; i++) {
                
                event = events[arguments[i]];
                if (event && event.resume) {
                    event.resume();
                }
            }
        },
        
        resumeEvents: function(discardQueue) {
            var me = this,
                queued = me.eventQueue,
                qLen, q;
            if (me.eventsSuspended && !--me.eventsSuspended) {
                delete me.eventQueue;
                if (!discardQueue && queued) {
                    qLen = queued.length;
                    for (q = 0; q < qLen; q++) {
                        
                        me.fireEventArgs.apply(me, queued[q]);
                    }
                }
            }
        },
        
        relayEvents: function(origin, events, prefix) {
            var me = this,
                len = events.length,
                i = 0,
                oldName, 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();
        },
        privates: {
            doAddListener: function(ename, fn, scope, options, order, caller, manager) {
                var me = this,
                    event, managedListeners, priority;
                order = order || (options && options.order);
                if (order) {
                    priority = (options && options.priority);
                    if (!priority) {
                        
                        
                        options = options ? Ext.Object.chain(options) : {};
                        options.priority = me.$orderToPriority[order];
                    }
                }
                ename = Ext.canonicalEventName(ename);
                
                if (!fn) {
                    Ext.raise("Cannot add '" + ename + "' listener to " + me.$className + " instance.  No function specified.");
                }
                
                if (!manager && (scope && scope.isObservable && (scope !== me))) {
                    manager = scope;
                }
                if (manager) {
                    
                    
                    managedListeners = manager.managedListeners = manager.managedListeners || [];
                    managedListeners.push({
                        item: me,
                        ename: ename,
                        fn: fn,
                        scope: scope,
                        options: options
                    });
                }
                event = (me.events || (me.events = {}))[ename];
                if (!event || !event.isEvent) {
                    event = me._initEvent(ename);
                }
                if (fn !== emptyFn) {
                    if (event.addListener(fn, scope, options, caller, manager)) {
                        
                        
                        me.hasListeners._incr_(ename);
                    }
                }
            },
            doRemoveListener: function(ename, fn, scope) {
                var me = this,
                    events = me.events,
                    event;
                ename = Ext.canonicalEventName(ename);
                event = events && events[ename];
                
                if (!fn) {
                    Ext.raise("Cannot remove '" + ename + "' listener to " + me.$className + " instance.  No function specified.");
                }
                
                if (event && event.isEvent) {
                    if (event.removeListener(fn, scope)) {
                        me.hasListeners._decr_(ename);
                    }
                }
            },
            _initEvent: function(eventName) {
                return (this.events[eventName] = new Ext.util.Event(this, eventName));
            }
        },
        deprecated: {
            '5.0': {
                methods: {
                    addEvents: null
                }
            }
        }
    };
}, 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 e = (this.methodEvents = this.methodEvents || {})[method],
            returnValue, v, cancel,
            obj = this,
            makeCall;
        if (!e) {
            this.methodEvents[method] = e = {};
            e.originalFn = this[method];
            e.methodName = method;
            e.before = [];
            e.after = [];
            makeCall = function(fn, scope, args) {
                if ((v = fn.apply(scope || obj, args)) !== undefined) {
                    if (typeof v == 'object') {
                        if (v.returnValue !== undefined) {
                            returnValue = v.returnValue;
                        } else {
                            returnValue = v;
                        }
                        cancel = !!v.cancel;
                    } else if (v === false) {
                        cancel = true;
                    } else {
                        returnValue = v;
                    }
                }
            };
            this[method] = function() {
                var args = Array.prototype.slice.call(arguments, 0),
                    b, i, len;
                returnValue = v = undefined;
                cancel = false;
                for (i = 0 , len = e.before.length; i < len; i++) {
                    b = e.before[i];
                    makeCall(b.fn, b.scope, args);
                    if (cancel) {
                        return returnValue;
                    }
                }
                if ((v = e.originalFn.apply(obj, args)) !== undefined) {
                    returnValue = v;
                }
                for (i = 0 , len = e.after.length; i < len; i++) {
                    b = e.after[i];
                    makeCall(b.fn, b.scope, args);
                    if (cancel) {
                        return returnValue;
                    }
                }
                return returnValue;
            };
        }
        return e;
    }
    Ext.apply(proto, {
        onClassMixedIn: prepareMixin,
        
        
        
        beforeMethod: function(method, fn, scope) {
            getMethodEvent.call(this, method).before.push({
                fn: fn,
                scope: scope
            });
        },
        
        afterMethod: function(method, fn, scope) {
            getMethodEvent.call(this, method).after.push({
                fn: fn,
                scope: scope
            });
        },
        removeMethodListener: function(method, fn, scope) {
            var e = this.getMethodEvent(method),
                i, len;
            for (i = 0 , len = e.before.length; i < len; i++) {
                if (e.before[i].fn == fn && e.before[i].scope == scope) {
                    Ext.Array.erase(e.before, i, 1);
                    return;
                }
            }
            for (i = 0 , len = e.after.length; i < len; i++) {
                if (e.after[i].fn == fn && e.after[i].scope == scope) {
                    Ext.Array.erase(e.after, i, 1);
                    return;
                }
            }
        },
        toggleEventLogging: function(toggle) {
            Ext.util.Observable[toggle ? 'capture' : 'releaseCapture'](this, function(en) {
                if (Ext.isDefined(Ext.global.console)) {
                    Ext.global.console.log(en, arguments);
                }
            });
        }
    });
});


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



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 ((Ext.isObject(value) || 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 (e) {
                    if (!isHandled) {
                        me.reject(e);
                    }
                }
            } else {
                me.complete('fulfill', value);
            }
        } catch (e) {
            me.reject(e);
        }
    },
    
    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 (Ext.isObject(value) || Ext.isFunction(value)) && Ext.isFunction(value.then);
            },
            
            rethrowError: function(error) {
                Ext.asap(function() {
                    throw error;
                });
            },
            
            when: function(value) {
                var deferred = new Ext.promise.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 = Ext.Function.bind(onFulfilled, scope);
                }
                if (onRejected) {
                    onRejected = Ext.Function.bind(onRejected, scope);
                }
                if (onProgress) {
                    onProgress = Ext.Function.bind(onProgress, scope);
                }
            }
            return this.owner.then(onFulfilled, onRejected, onProgress);
        },
        
        otherwise: 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 = Ext.Function.bind(onRejected, scope);
            }
            return this.owner.then(null, onRejected);
        },
        
        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 = Ext.Function.bind(onCompleted, 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() {
                
                
                Ext.raise("Not implemented");
            },
            
            
            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.Promise = P;
    } else {
        ExtPromise._ready();
    }
});



Ext.define('Ext.Deferred', function(Deferred) {
    var ExtPromise, 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, 0);
                deferred = new Deferred();
                setTimeout(function() {
                    deferred.resolve(promiseOrValue);
                }, milliseconds);
                return deferred.promise;
            },
            
            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);
            },
            
            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(value) {
                var deferred = new Ext.Deferred();
                deferred.resolve(value);
                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 = setTimeout(function() {
                    if (timeoutId) {
                        deferred.reject(new Error('Promise timed out.'));
                    }
                }, milliseconds);
                Deferred.resolved(promiseOrValue).then(function(value) {
                    clearTimeout(timeoutId);
                    timeoutId = null;
                    deferred.resolve(value);
                }, function(reason) {
                    clearTimeout(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;
};
Ext.Factory.prototype = {
    
    
    defaultProperty: 'type',
    
    
    instanceProp: 'isInstance',
    
    
    
    create: function(config, defaultType) {
        var me = this,
            Manager = Ext.ClassManager,
            cache = me.cache,
            alias, className, klass, suffix;
        if (config) {
            if (config[me.instanceProp]) {
                return config;
            }
            if (typeof config === 'string') {
                suffix = config;
                config = {};
                config[me.defaultProperty] = suffix;
            }
            className = config.xclass;
            suffix = config.type;
        }
        if (className) {
            if (!(klass = Manager.get(className))) {
                return Manager.instantiate(className, config);
            }
        } else {
            if (!(suffix = suffix || defaultType || me.defaultType)) {
                klass = me.defaultClass;
            }
            
            if (!suffix && !klass) {
                Ext.raise('No type specified for ' + me.type + '.create');
            }
            
            if (!klass && !(klass = cache[suffix])) {
                alias = me.aliasPrefix + suffix;
                className = Manager.getNameByAlias(alias);
                
                if (!(klass = className && Manager.get(className))) {
                    return Manager.instantiateByAlias(alias, config);
                }
                cache[suffix] = klass;
            }
        }
        return klass.isInstance ? klass : new klass(config);
    },
    fixNameRe: /\.[a-z]/ig,
    fixNameFn: function(match) {
        return match.substring(1).toUpperCase();
    },
    clearCache: function() {
        this.cache = {};
    }
};

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

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



Ext.define('Ext.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() {
        return (this.deferred = new Ext.Deferred());
    },
    
    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);
            }
        }
    },
    onTimeout: function() {
        var me = this;
        me.timedout = true;
        me.timer = null;
        me.abort(true);
    },
    getTimeout: function() {
        return this.timeout;
    },
    clearTimer: function() {
        var timer = this.timer;
        if (timer) {
            clearTimeout(timer);
            this.timer = null;
        }
    },
    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) {
            
            status = status == 1223 ? 204 : status;
            var success = (status >= 200 && status < 300) || status == 304,
                isException = false;
            if (!success) {
                switch (status) {
                    case 12002:
                    case 12029:
                    case 12030:
                    case 12031:
                    case 12152:
                    case 13030:
                        isException = true;
                        break;
                }
            }
            return {
                success: success,
                isException: isException
            };
        }
    },
    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 = Ext.Function.bind(me.onStateChange, me);
            }
        }
        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 = Ext.Function.bind(me.onStateChange, me, [
            true
        ]);
        xhr.onerror = xhr.ontimeout = Ext.Function.bind(me.onStateChange, me, [
            false
        ]);
    },
    processXdrResponse: function(response, xhr) {
        
        response.getAllResponseHeaders = function() {
            return [];
        };
        response.getResponseHeader = function() {
            return '';
        };
        response.contentType = xhr.contentType || this.defaultXdrContentType;
    },
    onStateChange: function(xdrResult) {
        var me = this,
            xhr = me.xhr,
            globalEvents = Ext.GlobalEvents;
        
        if ((xhr && xhr.readyState == 4) || me.isXdr) {
            me.clearTimer();
            me.onComplete(xdrResult);
            me.cleanup();
            if (globalEvents.hasListeners.idle) {
                globalEvents.fireEvent('idle');
            }
        }
    },
    
    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);
            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);
            owner.fireEvent('requestcomplete', owner, response, options);
            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);
            }
            owner.fireEvent('requestexception', owner, response, options);
            Ext.callback(options.failure, options.scope, [
                response,
                options
            ]);
        }
        me.result = response;
        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, request;
        
        frame.set({
            name: id,
            cls: Ext.baseCSSPrefix + 'hidden-display',
            src: Ext.SSL_SECURE_URL,
            tabIndex: -1
        });
        document.body.appendChild(frameDom);
        
        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();
        
        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 = '{success:false,message:"' + Ext.String.trim(response.statusText) + '"}';
            callback = options.failure;
            success = false;
        } else {
            try {
                doc = me.getDoc();
                
                me.result = response = {
                    responseText: '',
                    responseXML: null
                };
                
                
                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 = '{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);
        }
        Ext.callback(options.callback, options.scope, [
            options,
            undefined,
            undefined
        ]);
        return Ext.Deferred.rejected([
            options,
            undefined,
            undefined
        ]);
    },
    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 = Ext.Function.bind(me.run, me);
        
        
        
        
        
        if (Ext.os.is.iOS) {
            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')) {
                clearTimeout(me.idleTimer);
                delete me.idleTimer;
            }
            if (me.hasOwnProperty('idleQueueTimer')) {
                clearTimeout(me.idleQueueTimer);
                delete me.idleQueueTimer;
            }
            me.isIdle = false;
            me.isRunning = true;
            
            me.startCountTime = Ext.now();
            me.count = 0;
            
            me.doStart();
        }
    },
    watch: function() {
        if (this.isRunning && Ext.now() - this.lastRunTime >= 500) {
            this.run();
        }
    },
    run: function() {
        var me = this;
        if (!me.isRunning) {
            return;
        }
        var queue = me.runningQueue,
            now = Ext.now(),
            i, ln;
        me.lastRunTime = now;
        me.frameStartTime = now;
        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;
        }
        
        me.doIterate();
    },
    
    onFpsChanged: Ext.emptyFn,
    onStop: Ext.emptyFn,
    
    doStart: function() {
        this.animationFrameId = Ext.Function.requestAnimationFrame(this.run);
        this.lastRunTime = Ext.now();
    },
    doIterate: function() {
        this.animationFrameId = Ext.Function.requestAnimationFrame(this.run);
    },
    doStop: function() {
        Ext.Function.cancelAnimationFrame(this.animationFrameId);
    },
    
    stop: function(fn, scope, args) {
        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;
            me.idleTimer = Ext.defer(me.whenIdle, 100, me);
        }
    },
    onIdle: function(fn, scope, args) {
        var listeners = this.idleQueue,
            i, ln, listener;
        for (i = 0 , ln = listeners.length; i < ln; i++) {
            listener = listeners[i];
            if (fn === listener[0] && scope === listener[1] && args === listener[2]) {
                return;
            }
        }
        listeners.push(arguments);
        if (this.isIdle) {
            this.processIdleQueue();
        }
    },
    unIdle: function(fn, scope, args) {
        var listeners = this.idleQueue,
            i, ln, listener;
        for (i = 0 , ln = listeners.length; i < ln; i++) {
            listener = listeners[i];
            if (fn === listener[0] && scope === listener[1] && args === listener[2]) {
                listeners.splice(i, 1);
                return true;
            }
        }
        return false;
    },
    queueTask: function(fn, scope, args) {
        this.taskQueue.push(arguments);
        this.processTaskQueue();
    },
    dequeueTask: function(fn, scope, args) {
        var listeners = this.taskQueue,
            i, ln, listener;
        for (i = 0 , ln = listeners.length; i < ln; i++) {
            listener = listeners[i];
            if (fn === listener[0] && scope === listener[1] && args === listener[2]) {
                listeners.splice(i, 1);
                i--;
                ln--;
            }
        }
    },
    invoke: function(listener) {
        var fn = listener[0],
            scope = listener[1],
            args = listener[2];
        fn = (typeof fn == 'string' ? scope[fn] : fn);
        if (Ext.isArray(args)) {
            fn.apply(scope, args);
        } else {
            fn.call(scope, args);
        }
    },
    whenIdle: function() {
        this.isIdle = true;
        this.processIdleQueue();
    },
    processIdleQueue: function() {
        if (!this.hasOwnProperty('idleQueueTimer')) {
            this.idleQueueTimer = Ext.defer(this.processIdleQueueItem, 1, this);
        }
    },
    processIdleQueueItem: function() {
        delete this.idleQueueTimer;
        if (!this.isIdle) {
            return;
        }
        var listeners = this.idleQueue,
            listener;
        if (listeners.length > 0) {
            listener = listeners.shift();
            this.invoke(listener);
            this.processIdleQueue();
        }
    },
    processTaskQueue: function() {
        if (!this.hasOwnProperty('taskQueueTimer')) {
            this.taskQueueTimer = Ext.defer(this.processTaskQueueItem, 15, this);
        }
    },
    processTaskQueueItem: function() {
        delete this.taskQueueTimer;
        var listeners = this.taskQueue,
            listener;
        if (listeners.length > 0) {
            listener = listeners.shift();
            this.invoke(listener);
            this.processTaskQueue();
        }
    },
    
    
    showFps: function() {
        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(Ext.Function.bind(this.showFps, this));
    }
});



Ext.define('Ext.ComponentManager', {
    alternateClassName: 'Ext.ComponentMgr',
    singleton: true,
    count: 0,
    typeName: 'xtype',
    
    constructor: function(config) {
        var me = this;
        Ext.apply(me, config || {});
        me.all = {};
        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,
            all = me.all,
            key = component.getId(),
            onAvailableCallbacks = me.onAvailableCallbacks;
        
        if (key === undefined) {
            Ext.raise('Component id is undefined. Please ensure the component has an id.');
        }
        if (key in all) {
            Ext.raise('Registering duplicate component id "' + key + '"');
        }
        
        all[key] = component;
        if (component.getReference && component.getReference()) {
            me.references[key] = component;
        }
        ++me.count;
        if (!me.hasFocusListener) {
            Ext.on('focus', me.onGlobalFocus, me);
            me.hasFocusListener = true;
        }
        onAvailableCallbacks = onAvailableCallbacks && onAvailableCallbacks[key];
        if (onAvailableCallbacks && onAvailableCallbacks.length) {
            me.notifyAvailable(component);
        }
    },
    unregister: function(component) {
        var id = component.getId();
        if (component.getReference && component.getReference()) {
            this.references[id] = null;
            delete this.references[id];
        }
        this.all[id] = null;
        delete this.all[id];
        this.count--;
    },
    markReferencesDirty: function() {
        this.referencesDirty = true;
    },
    fixReferences: function() {
        var me = this,
            references = me.references,
            key;
        if (me.referencesDirty) {
            for (key in references) {
                if (references.hasOwnProperty(key)) {
                    references[key].fixReference();
                }
            }
            me.referencesDirty = false;
        }
    },
    
    onAvailable: function(id, fn, scope) {
        var me = this,
            callbacks = me.onAvailableCallbacks,
            all = me.all,
            item;
        if (id in all) {
            
            item = all[id];
            fn.call(scope || item, item);
        } else if (id) {
            
            if (!Ext.isArray(callbacks[id])) {
                callbacks[id] = [];
            }
            callbacks[id].push(function(item) {
                fn.call(scope || item, item);
            });
        }
    },
    
    notifyAvailable: function(item) {
        var callbacks = this.onAvailableCallbacks[item && item.getId()] || [];
        while (callbacks.length) {
            (callbacks.shift())(item);
        }
    },
    
    each: function(fn, scope) {
        return Ext.Object.each(this.all, fn, scope);
    },
    
    getCount: function() {
        return this.count;
    },
    
    getAll: function() {
        return Ext.Object.getValues(this.all);
    },
    
    getActiveComponent: function() {
        return Ext.Component.fromElement(Ext.dom.Element.getActiveElement());
    },
    
    onGlobalFocus: function(e) {
        var me = this,
            toElement = e.toElement,
            fromElement = e.fromElement,
            toComponent = Ext.Component.fromElement(toElement),
            fromComponent = Ext.Component.fromElement(fromElement),
            commonAncestor, targetComponent;
        
        if (toComponent === fromComponent) {
            return;
        }
        commonAncestor = me.getCommonAncestor(fromComponent, toComponent);
        if (fromComponent && !(fromComponent.destroyed || fromComponent.destroying)) {
            if (fromComponent.handleBlurEvent) {
                fromComponent.handleBlurEvent(e);
            }
            
            for (targetComponent = fromComponent; targetComponent && targetComponent !== commonAncestor; targetComponent = targetComponent.getRefOwner()) {
                if (!(targetComponent.destroyed || targetComponent.destroying)) {
                    targetComponent.onFocusLeave({
                        event: e.event,
                        type: 'focusleave',
                        target: fromElement,
                        relatedTarget: toElement,
                        fromComponent: fromComponent,
                        toComponent: toComponent
                    });
                }
            }
        }
        if (toComponent && !toComponent.destroyed) {
            if (toComponent.handleFocusEvent) {
                toComponent.handleFocusEvent(e);
            }
            
            for (targetComponent = toComponent; targetComponent && targetComponent !== commonAncestor; targetComponent = targetComponent.getRefOwner()) {
                targetComponent.onFocusEnter({
                    event: e.event,
                    type: 'focusenter',
                    relatedTarget: fromElement,
                    target: toElement,
                    fromComponent: fromComponent,
                    toComponent: toComponent
                });
            }
        }
    },
    getCommonAncestor: function(compA, compB) {
        if (compA === compB) {
            return compA;
        }
        while (compA && !(compA.isAncestor(compB) || compA === compB)) {
            compA = compA.getRefOwner();
        }
        return compA;
    },
    privates: {
        clearAll: function() {
            this.all = {};
            this.references = {};
            this.onAvailableCallbacks = {};
        },
        
        fromElement: function(node, limit, selector) {
            var target = Ext.getDom(node),
                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;
        }
    },
    deprecated: {
        5: {
            methods: {
                
                isRegistered: null,
                
                registerType: null
            }
        }
    }
}, function() {
    
    Ext.getCmp = function(id) {
        return Ext.ComponentManager.get(id);
    };
});


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


Ext.define('Ext.util.LruCache', {
    extend: Ext.util.HashMap,
    config: {
        
        maxSize: null
    },
    
    add: function(key, newValue) {
        var me = this,
            entry, last;
        me.removeAtKey(key);
        last = me.last;
        entry = {
            prev: last,
            next: null,
            key: key,
            value: newValue
        };
        if (last) {
            
            last.next = entry;
        } else {
            
            me.first = entry;
        }
        me.last = entry;
        me.callParent([
            key,
            entry
        ]);
        me.prune();
        return newValue;
    },
    
    insertBefore: function(key, newValue, sibling) {
        var me = this,
            existingKey, entry;
        
        
        if (sibling = this.map[this.findKey(sibling)]) {
            existingKey = me.findKey(newValue);
            
            if (existingKey) {
                me.unlinkEntry(entry = me.map[existingKey]);
            } else 
            {
                entry = {
                    prev: sibling.prev,
                    next: sibling,
                    key: key,
                    value: newValue
                };
            }
            if (sibling.prev) {
                entry.prev.next = entry;
            } else {
                me.first = entry;
            }
            entry.next = sibling;
            sibling.prev = entry;
            me.prune();
            return newValue;
        } else 
        {
            return me.add(key, newValue);
        }
    },
    
    get: function(key) {
        var entry = this.map[key];
        if (entry) {
            
            if (entry.next) {
                this.moveToEnd(entry);
            }
            return entry.value;
        }
    },
    
    removeAtKey: function(key) {
        this.unlinkEntry(this.map[key]);
        return this.callParent(arguments);
    },
    
    clear: function(
    initial) {
        this.first = this.last = null;
        return this.callParent([
            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);
            }
        }
    },
    destroy: function() {
        this.first = this.last = null;
        this.callParent();
    }
});






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.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.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 ? !!candidate[property] : queryOperators[operator](Ext.coerce(propValue, compareTo), compareTo)) {
                    result.push(candidate);
                }
            }
            return result;
        },
        
        filterById = function(items, id) {
            var result = [],
                i = 0,
                length = items.length,
                candidate;
            for (; i < length; i++) {
                candidate = items[i];
                if (candidate.getItemId() === id) {
                    result.push(candidate);
                }
            }
            return result;
        },
        
        filterByPseudo = function(items, name, value) {
            return cq.pseudos[name](items, value);
        },
        
        
        modeRe = /^(\s?([>\^])\s?|\s|$)/,
        
        tokenRe = /^(#)?((?:\\\.|[\w\-])+|\*)(?:\((true|false)\))?/,
        matchers = [
            {
                
                re: /^\.((?:\\\.|[\w\-])+)(?:\((true|false)\))?/,
                method: filterByXType,
                argTransform: function(args) {
                    
                    var selector = args[0];
                    Ext.log.warn('"' + selector + '" ComponentQuery selector style is deprecated,' + ' use "' + selector.replace(/^\./, '') + '" without the leading dot instead');
                    
                    if (args[1] !== undefined) {
                        args[1] = args[1].replace(unescapeRe, '$1');
                    }
                    return args.slice(1);
                }
            },
            {
                
                
                
                
                
                
                
                
                re: /^(?:\[((?:[@?$])?[\w\-]*)\s*(?:([\^$*~%!\/]?=)\s*(['"])?((?:\\\]|.)*?)\3)?(?!\\)\])/,
                method: filterByAttribute,
                argTransform: function(args) {
                    var selector = args[0],
                        property = args[1],
                        operator = args[2],
                        
                        compareTo = args[4],
                        compareRe;
                    
                    if (compareTo !== undefined) {
                        compareTo = compareTo.replace(unescapeRe, '$1');
                        
                        var format = Ext.String.format,
                            msg = "ComponentQuery selector '{0}' has an unescaped ({1}) character at the {2} " + "of the attribute value pattern. Usually that indicates an error " + "where the opening quote is not followed by the closing quote. " + "If you need to match a ({1}) character at the {2} of the attribute " + "value, escape the quote character in your pattern: (\\{1})",
                            match;
                        if (match = /^(['"]).*?[^'"]$/.exec(compareTo)) {
                            
                            Ext.log.warn(format(msg, selector, match[1], 'beginning'));
                        } else if (match = /^[^'"].*?(['"])$/.exec(compareTo)) {
                            
                            Ext.log.warn(format(msg, selector, match[1], 'end'));
                        }
                    }
                    
                    if (operator === '/=') {
                        compareRe = regexCache.get(compareTo);
                        if (compareRe) {
                            compareTo = compareRe;
                        } else {
                            compareTo = regexCache.add(compareTo, new RegExp(compareTo));
                        }
                    }
                    return [
                        property,
                        operator,
                        compareTo
                    ];
                }
            },
            {
                
                re: /^#((?:\\\.|[\w\-])+)/,
                method: filterById
            },
            {
                
                re: /^\:([\w\-]+)(?:\(((?:\{[^\}]+\})|(?:(?!\{)[^\s>\/]*?(?!\})))\))?/,
                method: filterByPseudo,
                argTransform: function(args) {
                    if (args[2] !== undefined) {
                        args[2] = args[2].replace(unescapeRe, '$1');
                    }
                    return args.slice(1);
                }
            },
            {
                
                re: /^(?:\{([^\}]+)\})/,
                method: filterFnPattern
            }
        ];
    
    cq.Query = Ext.extend(Object, {
        constructor: function(cfg) {
            cfg = cfg || {};
            Ext.apply(this, cfg);
        },
        
        
        
        
        
        
        
        
        execute: function(root) {
            var operations = this.operations,
                result = [],
                op, i, len;
            for (i = 0 , len = operations.length; i < len; i++) {
                op = operations[i];
                result = result.concat(this._execute(root, op));
            }
            return result;
        },
        _execute: function(root, operations) {
            var i = 0,
                length = operations.length,
                operation, workingItems;
            
            if (!root) {
                workingItems = Ext.ComponentManager.getAll();
            }
            
            else if (Ext.isIterable(root)) {
                workingItems = root;
            }
            
            else if (root.isMixedCollection) {
                workingItems = root.items;
            }
            
            
            for (; i < length; i++) {
                operation = operations[i];
                
                
                
                
                
                
                if (operation.mode === '^') {
                    workingItems = getAncestors(workingItems || [
                        root
                    ]);
                } else if (operation.mode) {
                    workingItems = getItems(workingItems || [
                        root
                    ], operation.mode);
                } else {
                    workingItems = filterItems(workingItems || getItems([
                        root
                    ]), operation);
                }
                
                
                if (i === length - 1) {
                    return workingItems;
                }
            }
            return [];
        },
        is: function(component) {
            var operations = this.operations,
                result = false,
                len = operations.length,
                op, i;
            if (len === 0) {
                return true;
            }
            for (i = 0; i < len; i++) {
                op = operations[i];
                result = this._is(component, op);
                if (result) {
                    return result;
                }
            }
            return false;
        },
        _is: function(component, operations) {
            var len = operations.length,
                active = [
                    component
                ],
                operation, i, j, mode, items, item;
            
            for (i = len - 1; i >= 0; --i) {
                operation = operations[i];
                mode = operation.mode;
                
                if (mode) {
                    if (mode === '^') {
                        active = getItems(active, ' ');
                    } else if (mode === '>') {
                        items = [];
                        for (j = 0 , len = active.length; j < len; ++j) {
                            item = active[j].getRefOwner();
                            if (item) {
                                items.push(item);
                            }
                        }
                        active = items;
                    } else {
                        active = getAncestors(active);
                    }
                    
                    if (active.length === 0) {
                        return false;
                    }
                } else {
                    active = filterItems(active, operation);
                    if (active.length === 0) {
                        return false;
                    }
                }
            }
            return true;
        },
        getMatches: function(components, operations) {
            var len = operations.length,
                i;
            for (i = 0; i < len; ++i) {
                components = filterItems(components, operations[i]);
                
                
                if (components.length === 0) {
                    break;
                }
            }
            return components;
        },
        isMultiMatch: function() {
            return this.operations.length > 1;
        }
    });
    Ext.apply(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;
            },
            "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;
            }
        },
        
        query: function(selector, root) {
            
            if (!selector) {
                return Ext.ComponentManager.all.getArray();
            }
            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) {
            if (!selector) {
                return true;
            }
            var query = cq.cache.get(selector);
            if (!query) {
                query = cq.cache.add(selector, cq.parse(selector));
            }
            return query.is(component);
        },
        parse: function(selector) {
            var operations = [],
                selectors, sel, i, len;
            selectors = Ext.splitAndUnescape(selector, ',');
            for (i = 0 , len = selectors.length; i < len; i++) {
                
                sel = Ext.String.trim(selectors[i]);
                
                
                
                
                if (sel === '') {
                    Ext.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.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'
    ],
    _alignRe: /^([a-z]+)-([a-z]+)([?!])?$/,
    
    
    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, mySize) {
        
        
        var me = this,
            el = me.el,
            doc = document,
            isViewport = (el.dom === doc.body || el.dom === doc),
            round = Math.round,
            xy, myWidth, myHeight;
        anchor = (anchor || "tl").toLowerCase();
        mySize = mySize || {};
        myWidth = mySize.width || (isViewport ? Ext.Element.getViewportWidth() : me.getWidth());
        myHeight = mySize.height || (isViewport ? Ext.Element.getViewportHeight() : me.getHeight());
        
        
        switch (anchor) {
            case 'tl':
                xy = [
                    0,
                    0
                ];
                break;
            case 'bl':
                xy = [
                    0,
                    myHeight
                ];
                break;
            case 'tr':
                xy = [
                    myWidth,
                    0
                ];
                break;
            case 'c':
                xy = [
                    round(myWidth * 0.5),
                    round(myHeight * 0.5)
                ];
                break;
            case 't':
                xy = [
                    round(myWidth * 0.5),
                    0
                ];
                break;
            case 'l':
                xy = [
                    0,
                    round(myHeight * 0.5)
                ];
                break;
            case 'r':
                xy = [
                    myWidth,
                    round(myHeight * 0.5)
                ];
                break;
            case 'b':
                xy = [
                    round(myWidth * 0.5),
                    myHeight
                ];
                break;
            case 'tc':
                xy = [
                    round(myWidth * 0.5),
                    0
                ];
                break;
            case 'bc':
                xy = [
                    round(myWidth * 0.5),
                    myHeight
                ];
                break;
            case 'br':
                xy = [
                    myWidth,
                    myHeight
                ];
        }
        return [
            xy[0] + extraX,
            xy[1] + extraY
        ];
    },
    
    convertPositionSpec: Ext.identityFn,
    
    getAlignToXY: function(alignToEl, posSpec, offset) {
        var me = this,
            constrainToEl, constrainTo, alignMatch, myPosition, alignToElPosition, myWidth, myHeight, alignToElRegion, swapY, swapX, constrain, align1, align2, p1y, p1x, p2y, p2x, x, y;
        alignToEl = Ext.get(alignToEl.el || alignToEl);
        if (!alignToEl || !alignToEl.dom) {
            
            Ext.raise({
                sourceClass: 'Ext.util.Positionable',
                sourceMethod: 'getAlignToXY',
                msg: 'Attempted to align an element that doesn\'t exist'
            });
        }
        
        offset = offset || [
            0,
            0
        ];
        posSpec = (!posSpec || posSpec === "?" ? "tl-bl?" : (!(/-/).test(posSpec) && posSpec !== "" ? "tl-" + posSpec : posSpec || "tl-bl")).toLowerCase();
        posSpec = me.convertPositionSpec(posSpec);
        alignMatch = posSpec.match(me._alignRe);
        
        if (!alignMatch) {
            Ext.raise({
                sourceClass: 'Ext.util.Positionable',
                sourceMethod: 'getAlignToXY',
                el: alignToEl,
                position: posSpec,
                offset: offset,
                msg: 'Attemmpted to align an element with an invalid position: "' + posSpec + '"'
            });
        }
        
        align1 = alignMatch[1];
        align2 = alignMatch[2];
        constrain = !!alignMatch[3];
        
        
        myPosition = me.getAnchorXY(align1, true);
        alignToElPosition = me.getAnchorToXY(alignToEl, align2, false);
        x = alignToElPosition[0] - myPosition[0] + offset[0];
        y = alignToElPosition[1] - myPosition[1] + offset[1];
        
        if (constrain) {
            
            
            if (alignMatch[3] === '!') {
                constrainToEl = alignToEl;
            } else {
                
                
                
                constrainToEl = me.constrainTo || me.container || me.el.parent();
            }
            constrainToEl = Ext.get(constrainToEl.el || constrainToEl);
            constrainTo = constrainToEl.getConstrainRegion();
            constrainTo.right = constrainTo.left + constrainToEl.el.dom.clientWidth;
            myWidth = me.getWidth();
            myHeight = me.getHeight();
            alignToElRegion = alignToEl.getRegion();
            
            
            
            
            p1y = align1.charAt(0);
            p1x = align1.charAt(align1.length - 1);
            p2y = align2.charAt(0);
            p2x = align2.charAt(align2.length - 1);
            
            
            swapY = (x < alignToElRegion.right && x + myWidth >= alignToElRegion.left) && ((p1y == "t" && p2y == "b") || (p1y == "b" && p2y == "t"));
            
            
            swapX = (y < alignToElRegion.bottom && y + myHeight >= alignToElRegion.top) && ((p1x == "r" && p2x == "l") || (p1x == "l" && p2x == "r"));
            if (x + myWidth > constrainTo.right) {
                if (swapX) {
                    x = alignToElRegion.left - myWidth;
                    
                    swapX = false;
                } else {
                    x = constrainTo.right - myWidth;
                }
            }
            if (x < constrainTo.left) {
                x = swapX ? alignToElRegion.right : constrainTo.left;
            }
            if (y + myHeight > constrainTo.bottom) {
                if (swapY) {
                    y = alignToElRegion.top - myHeight;
                    
                    swapY = false;
                } else {
                    y = constrainTo.bottom - myHeight;
                }
            }
            if (y < constrainTo.top) {
                y = swapY ? alignToElRegion.bottom : constrainTo.top;
            }
        }
        return [
            x,
            y
        ];
    },
    
    getAnchorXY: function(anchor, local, mySize) {
        var me = this,
            myPos = me.getXY(),
            el = me.el,
            doc = document,
            isViewport = el.dom == doc.body || el.dom == doc,
            scroll = el.getScroll(),
            extraX = isViewport ? scroll.left : local ? 0 : myPos[0],
            extraY = isViewport ? scroll.top : local ? 0 : myPos[1];
        return me.calculateAnchorXY(anchor, extraX, extraY, mySize);
    },
    
    getBox: function(contentBox, local) {
        var me = this,
            xy = local ? me.getLocalXY() : me.getXY(),
            x = xy[0],
            y = xy[1],
            w = me.getWidth(),
            h = me.getHeight(),
            borderPadding, beforeX, beforeY;
        if (contentBox) {
            borderPadding = me.getBorderPadding();
            beforeX = borderPadding.beforeX;
            beforeY = borderPadding.beforeY;
            x += beforeX;
            y += beforeY;
            w -= (beforeX + borderPadding.afterX);
            h -= (beforeY + borderPadding.afterY);
        }
        return {
            x: x,
            left: x,
            0: x,
            y: y,
            top: y,
            1: y,
            width: w,
            height: h,
            right: x + w,
            bottom: y + h
        };
    },
    
    calculateConstrainedPosition: function(constrainTo, proposedPosition, local, proposedSize) {
        var me = this,
            vector,
            fp = me.floatParent,
            parentNode = fp ? fp.getTargetEl() : null,
            parentOffset, borderPadding, proposedConstrainPosition,
            xy = false,
            localXY;
        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 thisRegion = this.getRegion(),
            vector = [
                0,
                0
            ],
            shadowSize = (this.shadow && this.constrainShadow && !this.shadowDisabled) ? this.shadow.getShadowSize() : undefined,
            overflowed = false,
            constrainSize,
            constraintInsets = this.constraintInsets;
        if (!(constrainTo instanceof Ext.util.Region)) {
            constrainTo = Ext.get(constrainTo.el || constrainTo);
            
            
            constrainSize = constrainTo.getViewSize();
            constrainTo = constrainTo.getConstrainRegion();
            constrainTo.right = constrainTo.left + constrainSize.width;
            constrainTo.bottom = constrainTo.top + constrainSize.height;
        }
        
        if (constraintInsets) {
            constraintInsets = Ext.isObject(constraintInsets) ? constraintInsets : Ext.Element.parseBox(constraintInsets);
            constrainTo.adjust(constraintInsets.top, constraintInsets.right, constraintInsets.bottom, constraintInsets.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 = Ext.fly(offsetsTo.el || offsetsTo).getXY();
        return [
            o[0] - e[0],
            o[1] - e[1]
        ];
    },
    
    getRegion: function() {
        var box = this.getBox();
        return new Ext.util.Region(box.top, box.right, box.bottom, box.left);
    },
    
    getClientRegion: function() {
        var me = this,
            scrollbarSize,
            viewContentBox = me.getBox(),
            myDom = me.dom;
        
        
        scrollbarSize = myDom.offsetWidth - myDom.clientWidth;
        if (scrollbarSize) {
            if (me.getStyle('direction') === 'rtl') {
                viewContentBox.left += scrollbarSize;
            } else {
                viewContentBox.right -= scrollbarSize;
            }
        }
        
        
        scrollbarSize = myDom.offsetHeight - myDom.clientHeight;
        if (scrollbarSize) {
            viewContentBox.bottom -= scrollbarSize;
        }
        
        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,
            translatedXY = [],
            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;
    }
});


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


Ext.define('Ext.dom.Underlay', {
    
    
    constructor: function(config) {
        Ext.apply(this, config);
    },
    
    beforeShow: Ext.emptyFn,
    
    getInsertionTarget: function() {
        return this.target;
    },
    
    getPool: function() {
        return this.pool || (this.self.prototype.pool = new Ext.dom.UnderlayPool(this.elementConfig));
    },
    
    hide: function() {
        var me = this,
            el = me.el;
        if (el) {
            el.hide();
            me.getPool().checkIn(el);
            me.el = null;
            me.hidden = true;
        }
    },
    
    realign: function(x, y, width, height) {
        var me = this,
            el = me.el,
            target = me.target,
            offsets = me.offsets,
            max = Math.max;
        if (el) {
            if (x == null) {
                x = target.getX();
            }
            if (y == null) {
                y = target.getY();
            }
            if (width == null) {
                width = target.getWidth();
            }
            if (height == null) {
                height = target.getHeight();
            }
            if (offsets) {
                x = 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,
            captures, directs, directCaptures;
        options = options || {};
        if (options.delegated === false || Ext.event.publisher.Dom.instance.directEvents[name]) {
            if (options.capture) {
                directCaptures = me.directCaptures || (me.directCaptures = new Ext.util.Event(me.observable, name));
                added = directCaptures.addListener(fn, scope, options, caller, manager);
            } else {
                directs = me.directs || (me.directs = new Ext.util.Event(me.observable, name));
                added = directs.addListener(fn, scope, options, caller, manager);
            }
        } else if (options.capture) {
            captures = me.captures || (me.captures = new Ext.util.Event(me.observable, name));
            added = captures.addListener(fn, scope, options, caller, manager);
        } else {
            added = me.callParent([
                fn,
                scope,
                options,
                caller,
                manager
            ]);
        }
        return added;
    },
    removeListener: function(fn, scope) {
        var me = this,
            captures = me.captures,
            directs = me.directs,
            directCaptures = me.directCaptures,
            removed = false,
            index = me.findListener(fn, scope);
        if (index !== -1) {
            removed = me.callParent([
                fn,
                scope,
                index
            ]);
        } else {
            if (directs) {
                index = directs.findListener(fn, scope);
            }
            if (index !== -1) {
                removed = directs.removeListener(fn, scope, index);
            } else {
                if (captures) {
                    index = captures.findListener(fn, scope);
                }
                if (index !== -1) {
                    removed = captures.removeListener(fn, scope, index);
                } else if (directCaptures) {
                    index = directCaptures.findListener(fn, scope);
                    if (index !== -1) {
                        removed = directCaptures.removeListener(fn, scope, index);
                    }
                }
            }
        }
        return removed;
    },
    clearListeners: function() {
        var me = this,
            directCaptures = me.directCaptures,
            directs = me.directs,
            captures = me.captures;
        if (directCaptures) {
            directCaptures.clearListeners();
        }
        if (directs) {
            directs.clearListeners();
        }
        if (captures) {
            captures.clearListeners();
        }
        me.callParent();
    },
    suspend: function() {
        var me = this,
            directCaptures = me.directCaptures,
            directs = me.directs,
            captures = me.captures;
        if (directCaptures) {
            directCaptures.suspend();
        }
        if (directs) {
            directs.suspend();
        }
        if (captures) {
            captures.suspend();
        }
        me.callParent();
    },
    resume: function() {
        var me = this,
            directCaptures = me.directCaptures,
            directs = me.directs,
            captures = me.captures;
        if (directCaptures) {
            directCaptures.resume();
        }
        if (directs) {
            directs.resume();
        }
        if (captures) {
            captures.resume();
        }
        me.callParent();
    }
});


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


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


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


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



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


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


Ext.define('Ext.util.SizeMonitor', {
    constructor: function(config) {
        var namespace = Ext.util.sizemonitor;
        if (Ext.browser.is.Firefox) {
            return new namespace.OverflowChange(config);
        } else {
            return new namespace.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];
        }
    },
    onElementResize: function(element, info) {
        Ext.TaskQueue.requestRead('fire', this, [
            element,
            'resize',
            [
                element,
                info
            ]
        ]);
    },
    
    
    
    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();
        }
    }
}, 
function(ElementSize) {
    ElementSize.instance = new ElementSize();
});


Ext.define('Ext.util.paintmonitor.Abstract', {
    config: {
        element: null,
        callback: Ext.emptyFn,
        scope: null,
        args: []
    },
    eventName: '',
    monitorClass: '',
    constructor: function(config) {
        this.onElementPainted = Ext.Function.bind(this.onElementPainted, this);
        this.initConfig(config);
    },
    bindListeners: function(bind) {
        this.monitorElement[bind ? 'addEventListener' : 'removeEventListener'](this.eventName, this.onElementPainted, true);
    },
    applyElement: function(element) {
        if (element) {
            return Ext.get(element);
        }
    },
    updateElement: function(element) {
        this.monitorElement = Ext.Element.create({
            classList: [
                Ext.baseCSSPrefix + 'paint-monitor',
                this.monitorClass
            ]
        }, true);
        element.appendChild(this.monitorElement);
        element.addCls(Ext.baseCSSPrefix + 'paint-monitored');
        this.bindListeners(true);
    },
    onElementPainted: function() {},
    destroy: function() {
        var 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];
        }
    },
    onElementPainted: function(element) {
        Ext.TaskQueue.requestRead('fire', this, [
            element,
            'painted',
            [
                element
            ]
        ]);
    }
}, function(ElementPaint) {
    ElementPaint.instance = new ElementPaint();
});


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


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
    },
    constructor: function() {
        var me = this;
        me.callParent();
        Ext.onInternalReady(function() {
            
            
            me.attachListeners();
        });
    },
    attachListeners: function() {
        Ext.get(window).on('resize', this.fireResize, this, {
            buffer: this.resizeBuffer
        });
        Ext.getDoc().on('mousedown', this.fireMouseDown, this);
    },
    fireMouseDown: function(e) {
        this.fireEvent('mousedown', 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;
            me.fireEvent('resize', w, h);
        }
    }
}, function(GlobalEvents) {
    
    Ext.on = function() {
        return GlobalEvents.addListener.apply(GlobalEvents, arguments);
    };
    
    Ext.un = function() {
        return GlobalEvents.removeListener.apply(GlobalEvents, arguments);
    };
});


Ext.USE_NATIVE_JSON = false;

Ext.JSON = (new (function() {
    
    
    
    var me = this,
        hasNative = window.JSON && JSON.toString() === '[object JSON]',
        useHasOwn = !!{}.hasOwnProperty,
        pad = function(n) {
            return n < 10 ? "0" + n : n;
        },
        doDecode = function(json) {
            return eval("(" + json + ')');
        },
        
        doEncode = function(o, newline) {
            
            if (o === null || o === undefined) {
                return "null";
            } else if (Ext.isDate(o)) {
                return me.encodeDate(o);
            } else if (Ext.isString(o)) {
                if (Ext.isMSDate(o)) {
                    return me.encodeMSDate(o);
                } else {
                    return me.encodeString(o);
                }
            } else if (typeof o === "number") {
                
                return isFinite(o) ? String(o) : "null";
            } else if (Ext.isBoolean(o)) {
                return String(o);
            }
            
            
            else if (o.toJSON) {
                return o.toJSON();
            } else if (Ext.isArray(o)) {
                return encodeArray(o, newline);
            } else if (Ext.isObject(o)) {
                return encodeObject(o, newline);
            } else if (typeof o === "function") {
                return "null";
            }
            return 'undefined';
        },
        m = {
            "\b": '\\b',
            "\t": '\\t',
            "\n": '\\n',
            "\f": '\\f',
            "\r": '\\r',
            '"': '\\"',
            "\\": '\\\\',
            '\v': '\\u000b'
        },
        
        charToReplace = /[\\\"\x00-\x1f\x7f-\uffff]/g,
        encodeString = function(s) {
            return '"' + s.replace(charToReplace, function(a) {
                var c = m[a];
                return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
            }) + '"';
        },
        encodeMSDate = function(o) {
            return '"' + o + '"';
        },
        encodeArrayPretty = function(o, newline) {
            var len = o.length,
                cnewline = newline + '   ',
                sep = ',' + cnewline,
                a = [
                    "[",
                    cnewline
                ],
                
                i;
            for (i = 0; i < len; i += 1) {
                a.push(me.encodeValue(o[i], cnewline), sep);
            }
            
            a[a.length - 1] = newline + ']';
            return a.join('');
        },
        encodeObjectPretty = function(o, newline) {
            var cnewline = newline + '   ',
                sep = ',' + cnewline,
                a = [
                    "{",
                    cnewline
                ],
                
                i, val;
            for (i in o) {
                val = o[i];
                if (!useHasOwn || o.hasOwnProperty(i)) {
                    
                    if (typeof val === 'function' || val === undefined) {
                        
                        continue;
                    }
                    a.push(me.encodeValue(i) + ': ' + me.encodeValue(val, cnewline), sep);
                }
            }
            
            a[a.length - 1] = newline + '}';
            return a.join('');
        },
        encodeArray = function(o, newline) {
            if (newline) {
                return encodeArrayPretty(o, newline);
            }
            var a = [
                    "[",
                    ""
                ],
                
                len = o.length,
                i;
            for (i = 0; i < len; i += 1) {
                a.push(me.encodeValue(o[i]), ',');
            }
            
            a[a.length - 1] = ']';
            return a.join("");
        },
        encodeObject = function(o, newline) {
            if (newline) {
                return encodeObjectPretty(o, newline);
            }
            var a = [
                    "{",
                    ""
                ],
                
                i, val;
            for (i in o) {
                val = o[i];
                if (!useHasOwn || o.hasOwnProperty(i)) {
                    
                    if (typeof val === 'function' || val === undefined) {
                        
                        continue;
                    }
                    a.push(me.encodeValue(i), ":", me.encodeValue(val), ',');
                }
            }
            
            a[a.length - 1] = '}';
            return a.join("");
        };
    
    me.encodeString = encodeString;
    
    me.encodeValue = doEncode;
    
    me.encodeDate = function(o) {
        return '"' + o.getFullYear() + "-" + pad(o.getMonth() + 1) + "-" + pad(o.getDate()) + "T" + pad(o.getHours()) + ":" + pad(o.getMinutes()) + ":" + pad(o.getSeconds()) + '"';
    };
    
    me.encode = function(o) {
        
        if (hasNative && Ext.USE_NATIVE_JSON) {
            return JSON.stringify(o);
        }
        return me.encodeValue(o);
    };
    
    me.decode = function(json, safe) {
        try {
            
            if (hasNative && Ext.USE_NATIVE_JSON) {
                return JSON.parse(json);
            }
            return doDecode(json);
        } catch (e) {
            if (safe) {
                return null;
            }
            Ext.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.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;
    },
    
    lookupReferenceHolder: function(skipThis) {
        return this.getInheritedConfig('referenceHolder', skipThis !== false) || null;
    },
    
    getRefOwner: function() {
        var me = this;
        
        
        return me.ownerCt || me.parent || me.$initParent || me.ownerCmp || me.floatParent;
    },
    
    
    invalidateInheritedState: function() {
        var inheritedState = this.inheritedState;
        if (inheritedState) {
            
            
            
            inheritedState.invalid = true;
            
            
            
            
            
            delete this.inheritedState;
        }
    },
    privates: {
        
        fixReference: function() {
            var me = this,
                refHolder;
            if (me.getReference()) {
                refHolder = me.lookupReferenceHolder();
                if (refHolder) {
                    refHolder.attachReference(me);
                }
            }
        },
        
        onInheritedAdd: function(parent, instanced) {
            var me = this;
            
            
            
            if (me.inheritedState && instanced) {
                me.invalidateInheritedState();
            }
            if (me.getReference()) {
                Ext.ComponentManager.markReferencesDirty();
            }
        },
        
        onInheritedRemove: function(destroying) {
            var me = this,
                refHolder;
            if (me.getReference()) {
                refHolder = me.lookupReferenceHolder();
                if (refHolder) {
                    refHolder.clearReference(me);
                }
            }
            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);
            }
        },
        
        reference: null,
        
        
        session: {
            $value: null,
            lazy: true
        },
        
        twoWayBindable: {
            $value: null,
            lazy: true,
            merge: function(newValue, oldValue) {
                return this.mergeSets(newValue, oldValue);
            }
        },
        
        
        viewModel: {
            $value: null,
            lazy: true
        }
    },
    
    defaultBindProperty: null,
    
    validRefRe: /^[a-z_][a-z0-9_]*$/i,
    
    initInheritedState: function(inheritedState) {
        var me = this,
            reference = me.getReference(),
            controller = me.getController(),
            
            
            viewModel = me.getConfig('viewModel', true),
            session = me.getConfig('session', true),
            defaultListenerScope = me.getDefaultListenerScope();
        if (controller) {
            inheritedState.controller = controller;
        }
        if (defaultListenerScope) {
            inheritedState.defaultListenerScope = me;
        } else if (controller) {
            inheritedState.defaultListenerScope = controller;
        }
        if (viewModel) {
            
            
            
            
            if (!viewModel.isViewModel) {
                viewModel = me;
            }
            inheritedState.viewModel = viewModel;
        }
        
        if (session) {
            if (!session.isSession) {
                session = me;
            }
            inheritedState.session = session;
        }
        if (reference) {
            me.referenceKey = (inheritedState.referencePath || '') + reference;
            me.viewModelKey = (inheritedState.viewModelPath || '') + reference;
        }
    },
    
    lookupController: function(skipThis) {
        return this.getInheritedConfig('controller', skipThis) || null;
    },
    
    lookupSession: function(skipThis) {
        
        var ret = skipThis ? null : this.getSession();
        
        if (!ret) {
            ret = this.getInheritedConfig('session', skipThis);
            if (ret && !ret.isSession) {
                ret = ret.getInherited().session = ret.getSession();
            }
        }
        return ret || null;
    },
    
    lookupViewModel: function(skipThis) {
        var ret = skipThis ? null : this.getViewModel();
        
        if (!ret) {
            ret = this.getInheritedConfig('viewModel', skipThis);
            
            
            
            if (ret && !ret.isViewModel) {
                ret = ret.getInherited().viewModel = ret.getViewModel();
            }
        }
        return ret || null;
    },
    
    publishState: function(property, value) {
        var me = this,
            state = me.publishedState,
            binds = me.getBind(),
            binding = binds && property && binds[property],
            count = 0,
            name, publishes, vm, path;
        if (binding && !binding.syncing && !binding.isReadOnly()) {
            
            
            
            
            
            
            if (!(binding.calls === 0 && (value == null || value === me.getInitialConfig()[property]))) {
                binding.setValue(value);
            }
        }
        if (!(publishes = me.getPublishes())) {
            return;
        }
        if (!(vm = me.lookupViewModel())) {
            return;
        }
        
        
        if (!(path = me.viewModelKey)) {
            return;
        }
        if (property && state) {
            if (!publishes[property]) {
                return;
            }
            
            
            if (!(value && value.constructor === Object) && !(value instanceof Array)) {
                if (state[property] === value) {
                    return;
                }
            }
            path += '.';
            path += property;
        } else {
            state = state || (me.publishedState = {});
            for (name in publishes) {
                ++count;
                
                
                if (name === property) {
                    state[name] = value;
                } else {
                    state[name] = me[name];
                }
            }
            if (!count) {
                
                return;
            }
            value = state;
        }
        vm.set(path, value);
    },
    
    privates: {
        
        addBindableUpdater: function(property) {
            var me = this,
                configs = me.self.$config.configs,
                cfg = configs[property],
                updateName;
            
            
            if (cfg && !me.hasOwnProperty(updateName = cfg.names.update)) {
                me[updateName] = cfg.bindableUpdater || (cfg.root.bindableUpdater = me.makeBindableUpdater(cfg));
            }
        },
        
        applyBind: function(binds, currentBindings) {
            if (!binds) {
                return binds;
            }
            var me = this,
                viewModel = me.lookupViewModel(),
                twoWayable = me.getTwoWayBindable(),
                getBindTemplateScope = me._getBindTemplateScope,
                b, property, descriptor;
            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 && typeof b !== 'string') {
                    b.destroy();
                    b = null;
                }
                if (descriptor) {
                    b = viewModel.bind(descriptor, me.onBindNotify, me);
                    b._config = Ext.Config.get(property);
                    b.getTemplateScope = getBindTemplateScope;
                    
                    if (!me[b._config.names.set]) {
                        Ext.raise('Cannot bind ' + property + ' on ' + me.$className + ' - missing a ' + b._config.names.set + ' method.');
                    }
                }
                
                currentBindings[property] = b;
                if (twoWayable && twoWayable[property] && !b.isReadOnly()) {
                    me.addBindableUpdater(property);
                }
            }
            return currentBindings;
        },
        applyController: function(controller) {
            if (controller) {
                controller = Ext.Factory.controller(controller);
                controller.setView(this);
            }
            return controller;
        },
        applyPublishes: function(all) {
            if (this.lookupViewModel()) {
                for (var property in all) {
                    this.addBindableUpdater(property);
                }
            }
            return all;
        },
        
        applyReference: function(reference) {
            var validIdRe = this.validRefRe || Ext.validIdRe;
            if (reference && !validIdRe.test(reference)) {
                Ext.raise('Invalid reference "' + reference + '" for ' + this.getId() + ' - not a valid identifier');
            }
            return reference;
        },
        
        
        applySession: function(session) {
            if (!session) {
                return null;
            }
            if (!session.isSession) {
                var parentSession = this.lookupSession(true),
                    
                    config = (session === true) ? {} : session;
                if (parentSession) {
                    session = parentSession.spawn(config);
                } else {
                    
                    
                    session = new Ext.data['Session'](config);
                }
            }
            return session;
        },
        
        applyViewModel: function(viewModel) {
            var me = this,
                config, session;
            if (!viewModel) {
                return null;
            }
            if (!viewModel.isViewModel) {
                config = {
                    parent: me.lookupViewModel(true)
                };
                
                config.session = me.getSession();
                if (!session && !config.parent) {
                    config.session = me.lookupSession();
                }
                if (viewModel) {
                    if (viewModel.constructor === Object) {
                        Ext.apply(config, viewModel);
                    } else if (typeof viewModel === 'string') {
                        config.type = viewModel;
                    }
                }
                viewModel = Ext.Factory.viewModel(config);
            }
            return viewModel;
        },
        _getBindTemplateScope: function() {
            
            
            return this.scope.resolveListenerScope();
        },
        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() {
            this.initBindable = Ext.emptyFn;
            this.getBind();
            this.getPublishes();
        },
        
        
        
        
        
        
        
        
        
        
        
        
        
        makeBindableUpdater: function(cfg) {
            var updateName = cfg.names.update;
            return function(newValue, oldValue) {
                var me = this,
                    updater = me.self.prototype[updateName];
                if (updater) {
                    updater.call(me, newValue, oldValue);
                }
                me.publishState(cfg.name, newValue);
            };
        },
        
        isSyncing: function(name) {
            var bindings = this.getBind(),
                ret = false,
                binding;
            if (bindings) {
                binding = bindings[name];
                if (binding) {
                    ret = binding.syncing > 0;
                }
            }
            return ret;
        },
        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.destroying) {
                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) {
            var state = this.getInherited(),
                controller = this.getController();
            if (viewModel) {
                state.viewModel = viewModel;
                viewModel.setView(this);
                if (controller) {
                    controller.initViewModel(viewModel);
                }
            } else {
                delete state.viewModel;
            }
        }
    }
});



Ext.define('Ext.mixin.ComponentDelegation', {
    extend: Ext.Mixin,
    mixinConfig: {
        id: 'componentDelegation'
    },
    privates: {
        
        addDelegatedListener: function(eventName, fn, scope, options, order, caller, manager) {
            var me = this,
                delegatedEvents, event, priority;
            
            
            
            
            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];
                    }
                }
            }
        }
    },
    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.Widget', {
    extend: Ext.Evented,
    xtype: 'widget',
    mixins: [
        Ext.mixin.Inheritable,
        Ext.mixin.Bindable,
        Ext.mixin.ComponentDelegation
    ],
    isWidget: true,
    
    element: {
        reference: 'element'
    },
    observableType: 'component',
    cachedConfig: {
        
        style: null
    },
    config: {
        
        userCls: null
    },
    eventedConfig: {
        
        width: null,
        
        height: null
    },
    
    template: [],
    constructor: function(config) {
        var me = this,
            controller;
        me.initId(config);
        me.initElement();
        me.mixins.observable.constructor.call(me, config);
        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');
        }
    },
    addCls: function(cls) {
        this.el.addCls(cls);
    },
    applyWidth: function(width) {
        return this.filterLengthValue(width);
    },
    applyHeight: function(height) {
        return this.filterLengthValue(height);
    },
    clearListeners: function() {
        var me = this;
        me.mixins.observable.clearListeners.call(me);
        me.mixins.componentDelegation.clearDelegatedListeners.call(me);
    },
    destroy: function() {
        var me = this,
            referenceList = me.referenceList,
            i, ln, reference;
        
        for (i = 0 , ln = referenceList.length; i < ln; i++) {
            reference = referenceList[i];
            if (me.hasOwnProperty(reference)) {
                me[reference].destroy();
                me[reference] = null;
            }
        }
        me.destroyBindable();
        me.callParent();
        Ext.ComponentManager.unregister(me);
    },
    doFireEvent: function(eventName, args, bubbles) {
        var me = this,
            ret = me.mixins.observable.doFireEvent.call(me, eventName, args, bubbles);
        if (ret !== false) {
            ret = me.mixins.componentDelegation.doFireDelegatedEvent.call(me, eventName, args);
        }
        return ret;
    },
    
    getElementConfig: function() {
        var me = this,
            el = me.element;
        if (!('children' in el)) {
            el = Ext.apply({
                children: me.getTemplate()
            }, el);
        }
        return el;
    },
    
    getSize: function() {
        return {
            width: this.getWidth(),
            height: this.getHeight()
        };
    },
    getTemplate: function() {
        return this.template;
    },
    
    initElement: function() {
        var me = this,
            prototype = me.self.prototype,
            id = me.getId(),
            
            
            
            referenceList = me.referenceList = me.referenceList = [],
            cleanAttributes = true,
            renderTemplate, renderElement, element, referenceNodes, i, ln, referenceNode, reference;
        if (prototype.hasOwnProperty('renderTemplate')) {
            
            
            
            
            renderTemplate = me.renderTemplate.cloneNode(true);
            renderElement = renderTemplate.firstChild;
        } else {
            
            
            cleanAttributes = false;
            renderTemplate = document.createDocumentFragment();
            renderElement = Ext.Element.create(me.processElementConfig.call(prototype), true);
            renderTemplate.appendChild(renderElement);
        }
        referenceNodes = renderTemplate.querySelectorAll('[reference]');
        for (i = 0 , ln = referenceNodes.length; i < ln; i++) {
            referenceNode = referenceNodes[i];
            reference = referenceNode.getAttribute('reference');
            if (cleanAttributes) {
                
                
                
                
                
                
                
                referenceNode.removeAttribute('reference');
            }
            if (reference === 'element') {
                
                if (element) {
                    
                    Ext.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);
            } else {
                me.addElementReferenceOnDemand(reference, 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);
        }
    },
    
    is: function(selector) {
        return Ext.ComponentQuery.is(this, selector);
    },
    
    isXType: function(xtype, shallow) {
        return shallow ? (Ext.Array.indexOf(this.xtypes, xtype) !== -1) : !!this.xtypesMap[xtype];
    },
    removeCls: function(cls) {
        this.el.removeCls(cls);
    },
    
    toggleCls: function(cls, state) {
        this.element.toggleCls(cls, state);
    },
    resolveListenerScope: function(defaultScope, skipThis) {
        
        return this.mixins.inheritable.resolveListenerScope.call(this, defaultScope, skipThis);
    },
    
    setSize: function(width, height) {
        if (width !== undefined) {
            this.setWidth(width);
        }
        if (height !== undefined) {
            this.setHeight(height);
        }
    },
    
    applyStyle: function(style, oldStyle) {
        
        
        
        
        
        
        if (oldStyle && style === oldStyle && Ext.isObject(oldStyle)) {
            style = Ext.apply({}, style);
        }
        return style;
    },
    
    updateStyle: function(style) {
        this.element.applyStyles(style);
    },
    
    updateWidth: function(width) {
        this.element.setWidth(width);
    },
    
    updateHeight: function(height) {
        this.element.setHeight(height);
    },
    
    
    onFocusEnter: Ext.emptyFn,
    onFocusLeave: Ext.emptyFn,
    isAncestor: function() {
        return false;
    },
    
    privates: {
        
        addElementReferenceOnDemand: function(name, domNode) {
            if (this._elementListeners[name]) {
                
                
                
                this.addElementReference(name, domNode);
            } else {
                
                
                Ext.Object.defineProperty(this, name, {
                    get: function() {
                        
                        
                        
                        delete this[name];
                        return this.addElementReference(name, domNode);
                    },
                    configurable: true
                });
            }
        },
        
        addElementReference: function(name, domNode) {
            var me = this,
                referenceEl = me[name] = Ext.get(domNode),
                listeners = me._elementListeners[name],
                eventName, listener;
            referenceEl.skipGarbageCollection = true;
            referenceEl.component = me;
            if (listeners) {
                
                
                
                
                
                
                
                
                
                listeners = Ext.clone(listeners);
                
                
                
                
                
                
                
                
                
                
                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);
            this.isDetached = true;
        },
        
        doAddListener: function(name, fn, scope, options, order, caller, manager) {
            var me = this,
                delegate;
            if (options && 'element' in options) {
                
                if (me.referenceList.indexOf(options.element) === -1) {
                    Ext.Logger.error("Adding event listener with an invalid element reference of '" + options.element + "' for this component. Available values are: '" + me.referenceList.join("', '") + "'", me);
                }
                
                
                me[options.element].doAddListener(name, fn, scope || me, options, order);
            }
            if (options) {
                delegate = options.delegate;
                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 === 'auto' || (!value && value !== 0)) {
                return null;
            }
            return value;
        },
        getFocusEl: function() {
            return this.element;
        },
        
        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();
            }
        },
        
        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;
        },
        reattachToBody: function() {
            
            this.isDetached = false;
        },
        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.define('Ext.mixin.Traversable', {
    extend: Ext.Mixin,
    mixinConfig: {
        id: 'traversable'
    },
    setParent: function(parent) {
        this.parent = parent;
        return this;
    },
    
    hasParent: function() {
        return Boolean(this.parent);
    },
    
    getParent: function() {
        return this.parent;
    },
    getAncestors: function() {
        var ancestors = [],
            parent = this.getParent();
        while (parent) {
            ancestors.push(parent);
            parent = parent.getParent();
        }
        return ancestors;
    },
    getAncestorIds: function() {
        var ancestorIds = [],
            parent = this.getParent();
        while (parent) {
            ancestorIds.push(parent.getId());
            parent = parent.getParent();
        }
        return ancestorIds;
    }
});


Ext.define('Ext.overrides.Widget', {
    override: 'Ext.Widget',
    mixins: [
        Ext.mixin.Traversable
    ],
    config: {
        
        flex: {
            evented: true,
            $value: null
        },
        
        
        itemId: undefined
    },
    constructor: function(config) {
        this.callParent([
            config
        ]);
        this.initBindable();
    },
    applyFlex: function(flex) {
        if (flex) {
            flex = Number(flex);
            if (isNaN(flex)) {
                flex = null;
            }
        } else {
            flex = null;
        }
        return flex;
    },
    applyItemId: function(itemId) {
        return itemId || this.getId();
    },
    render: function(container, insertBeforeElement) {
        this.renderTo(container, insertBeforeElement);
    },
    renderTo: function(container, insertBeforeElement) {
        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(Boolean(dom.offsetParent));
        }
    },
    destroy: function() {
        var me = this,
            parent = me.getParent();
        if (parent && parent.remove) {
            parent.remove(me, false);
        }
        me.callParent();
    },
    isInnerItem: function() {
        return true;
    },
    isCentered: function() {
        return false;
    },
    isDocked: function() {
        return Boolean(this.getDocked());
    },
    isFloating: function() {
        return false;
    },
    getDocked: function() {
        return this._docked;
    },
    
    onAdded: function(parent, instanced) {
        var me = this,
            inheritedState = me.inheritedState,
            currentParent = me.parent;
        if (currentParent && currentParent !== parent) {
            currentParent.remove(me, false);
        }
        me.parent = parent;
        me.onInheritedAdd(parent, instanced);
    },
    onRemoved: function(destroying) {
        if (!destroying) {
            this.removeBindings();
        }
        this.onInheritedRemove(destroying);
        this.parent = null;
    },
    setLayoutSizeFlags: Ext.emptyFn,
    
    setRendered: function(rendered) {
        var wasRendered = this.rendered;
        if (rendered !== wasRendered) {
            this.rendered = rendered;
            return true;
        }
        return false;
    },
    updateLayout: function() {
        
        
        
        var parent = this.getParent(),
            scrollable;
        if (parent) {
            scrollable = parent.getScrollable();
            if (scrollable) {
                scrollable.refresh();
            }
        }
    }
});


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.Widget,
    xtype: [
        'progress',
        'progressbarwidget'
    ],
    alternateClassName: 'Ext.ProgressBarWidget',
    mixins: [
        Ext.ProgressBase
    ],
    config: {
        
        text: null,
        
        animate: false
    },
    cachedConfig: {
        
        baseCls: Ext.baseCSSPrefix + 'progress',
        textCls: Ext.baseCSSPrefix + 'progress-text',
        cls: null,
        ui: null
    },
    template: [
        {
            reference: 'backgroundEl'
        },
        {
            reference: 'barEl',
            children: [
                {
                    reference: 'textEl'
                }
            ]
        }
    ],
    defaultBindProperty: 'value',
    updateWidth: function(width, oldWidth) {
        var me = this;
        me.callParent([
            width,
            oldWidth
        ]);
        width -= me.element.getBorderWidth('lr');
        me.backgroundEl.setWidth(width);
        me.textEl.setWidth(width);
    },
    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.getBaseCls() + '-';
        if (oldUi) {
            element.removeCls(baseCls + oldUi);
            barEl.removeCls(baseCls + 'bar-' + oldUi);
        }
        element.addCls(baseCls + ui);
        barEl.addCls(baseCls + 'bar-' + ui);
    },
    updateBaseCls: function(baseCls, oldBaseCls) {
        
        if (oldBaseCls) {
            Ext.raise('You cannot configure baseCls - use a subclass');
        }
        
        this.element.addCls(baseCls);
        this.barEl.addCls(baseCls + '-bar');
    },
    updateTextCls: function(textCls) {
        this.backgroundEl.addCls(textCls + ' ' + textCls + '-back');
        this.textEl.addCls(textCls);
    },
    updateValue: function(value, oldValue) {
        var me = this,
            barEl = me.barEl,
            textTpl = me.getTextTpl();
        if (textTpl) {
            me.setText(textTpl.apply({
                value: value,
                percent: Math.round(value * 100)
            }));
        }
        if (me.getAnimate()) {
            barEl.stopAnimation();
            barEl.animate(Ext.apply({
                from: {
                    width: (oldValue * 100) + '%'
                },
                to: {
                    width: (value * 100) + '%'
                }
            }, me.animate));
        } else {
            barEl.setStyle('width', (value * 100) + '%');
        }
    },
    updateText: function(text) {
        this.backgroundEl.setHtml(text);
        this.textEl.setHtml(text);
    }
});


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


Ext.define('Ext.Template', {
    inheritableStatics: {
        
        from: function(el, config) {
            el = Ext.getDom(el);
            return new this(el.value || el.innerHTML, config || '');
        }
    },
    
    
    
    useEval: Ext.isGecko,
    
    
    constructor: function(html) {
        var me = this,
            args = arguments,
            buffer = [],
            i,
            length = args.length,
            value;
        me.initialConfig = {};
        
        
        
        if (length === 1 && Ext.isArray(html)) {
            args = html;
            length = args.length;
        }
        if (length > 1) {
            for (i = 0; i < length; i++) {
                value = args[i];
                if (typeof value === 'object') {
                    Ext.apply(me.initialConfig, value);
                    Ext.apply(me, value);
                } else {
                    buffer.push(value);
                }
            }
        } else {
            buffer.push(html);
        }
        
        me.html = buffer.join('');
    },
    
    isTemplate: true,
    
    
    disableFormats: false,
    
    tokenRe: /\{(?:(?:(\d+)|([a-z_][\w\-]*))(?::([a-z_\.]+)(?:\(([^\)]*?)?\))?)?)\}/gi,
    
    apply: function(values) {
        var me = this;
        if (me.compiled) {
            if (!me.fn) {
                me.compile();
            }
            return me.fn(values).join('');
        }
        return me.evaluate(values);
    },
    
    evaluate: function(values) {
        var me = this,
            useFormat = !me.disableFormats,
            fm = Ext.util.Format,
            tpl = me;
        function fn(match, index, name, formatFn, args) {
            
            
            if (name == null || name === '') {
                name = index;
            }
            if (formatFn && useFormat) {
                if (args) {
                    args = [
                        values[name]
                    ].concat(Ext.functionFactory('return [' + args + '];')());
                } else {
                    args = [
                        values[name]
                    ];
                }
                
                if (formatFn.substr(0, 5) === "this.") {
                    return tpl[formatFn.substr(5)].apply(tpl, args);
                }
                
                else if (fm[formatFn]) {
                    return fm[formatFn].apply(fm, args);
                } else 
                {
                    return match;
                }
            } else {
                return values[name] !== undefined ? values[name] : "";
            }
        }
        return me.html.replace(me.tokenRe, fn);
    },
    
    applyOut: function(values, out) {
        var me = this;
        if (me.compiled) {
            if (!me.fn) {
                me.compile();
            }
            out.push.apply(out, me.fn(values));
        } else {
            out.push(me.apply(values));
        }
        return out;
    },
    
    applyTemplate: function() {
        return this.apply.apply(this, arguments);
    },
    
    set: function(html, compile) {
        var me = this;
        me.html = html;
        me.compiled = !!compile;
        me.fn = null;
        return me;
    },
    compileARe: /\\/g,
    compileBRe: /(\r\n|\n)/g,
    compileCRe: /'/g,
    
    compile: function() {
        var me = this,
            code;
        code = me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.tokenRe, me.regexReplaceFn.bind(me));
        code = (this.disableFormats !== true ? 'var fm=Ext.util.Format;' : '') + (me.useEval ? '$=' : 'return') + " function(v){return ['" + code + "'];};";
        me.fn = me.useEval ? me.evalCompiled(code) : (new Function('Ext', code))(Ext);
        
        me.compiled = true;
        return me;
    },
    
    evalCompiled: function($) {
        
        
        
        
        eval($);
        
        return $;
    },
    regexReplaceFn: function(match, index, name, formatFn, args) {
        
        
        
        if (index == null || index === '') {
            index = '"' + name + '"';
        }
        
        
        else if (this.stringFormat) {
            index = parseInt(index) + 1;
        }
        if (formatFn && this.disableFormats !== true) {
            args = args ? ',' + args : "";
            
            if (formatFn.substr(0, 5) === "this.") {
                formatFn = formatFn + '(';
            }
            
            else if (Ext.util.Format[formatFn]) {
                formatFn = "fm." + formatFn + '(';
            } else 
            {
                return match;
            }
            return "'," + formatFn + "v[" + index + "]" + args + "),'";
        } else {
            return "',v[" + index + "] == undefined ? '' : v[" + index + "],'";
        }
    },
    
    insertFirst: function(el, values, returnElement) {
        return this.doInsert('afterBegin', el, values, returnElement);
    },
    
    insertBefore: function(el, values, returnElement) {
        return this.doInsert('beforeBegin', el, values, returnElement);
    },
    
    insertAfter: function(el, values, returnElement) {
        return this.doInsert('afterEnd', el, values, returnElement);
    },
    
    append: function(el, values, returnElement) {
        return this.doInsert('beforeEnd', el, values, returnElement);
    },
    doInsert: function(where, el, values, returnElement) {
        var newNode = Ext.DomHelper.insertHtml(where, Ext.getDom(el), this.apply(values));
        return returnElement ? Ext.get(newNode) : newNode;
    },
    
    overwrite: function(el, values, returnElement) {
        var newNode = Ext.DomHelper.overwrite(Ext.getDom(el), this.apply(values));
        return returnElement ? Ext.get(newNode) : newNode;
    }
}, function(Template) {
    var formatRe = /\{\d+\}/,
        generateFormatFn = function(format) {
            
            if (formatRe.test(format)) {
                format = new Template(format, formatTplConfig);
                return function() {
                    return format.apply(arguments);
                };
            } else 
            {
                return function() {
                    return format;
                };
            }
        },
        
        
        
        formatTplConfig = {
            useFormat: false,
            compiled: true,
            stringFormat: true
        },
        formatFns = {};
    
    
    Ext.String.format = Ext.util.Format.format = function(format) {
        var formatFn = formatFns[format] || (formatFns[format] = generateFormatFn(format));
        return formatFn.apply(this, arguments);
    };
    Ext.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;
        delete me.definitions;
        delete me.body;
        delete me.switches;
        return code;
    },
    
    
    doText: function(text) {
        var me = this,
            out = me.body;
        text = text.replace(me.aposRe, "\\'").replace(me.newLineRe, '\\n');
        if (me.useIndex) {
            out.push('out[out.length]=\'', text, '\'\n');
        } else {
            out.push('out.push(\'', text, '\')\n');
        }
    },
    doExpr: function(expr) {
        var out = this.body;
        out.push('if ((v=' + expr + ') != null) out');
        
        
        if (this.useIndex) {
            out.push('[out.length]=v+\'\'\n');
        } else {
            out.push('.push(v+\'\')\n');
        }
    },
    doTag: function(tag) {
        var expr = this.parseTag(tag);
        if (expr) {
            this.doExpr(expr);
        } else {
            
            this.doText('{' + tag + '}');
        }
    },
    doElse: function() {
        this.body.push('} else {\n');
    },
    doEval: function(text) {
        this.body.push(text, '\n');
    },
    doIf: function(action, actions) {
        var me = this;
        
        if (action === '.') {
            me.body.push('if (values) {\n');
        } else if (me.propNameRe.test(action)) {
            me.body.push('if (', me.parseTag(action), ') {\n');
        } else 
        {
            me.body.push('if (', me.addFn(action), me.callFn, ') {\n');
        }
        if (actions.exec) {
            me.doExec(actions.exec);
        }
    },
    doElseIf: function(action, actions) {
        var me = this;
        
        if (action === '.') {
            me.body.push('else if (values) {\n');
        } else if (me.propNameRe.test(action)) {
            me.body.push('} else if (', me.parseTag(action), ') {\n');
        } else 
        {
            me.body.push('} else if (', me.addFn(action), me.callFn, ') {\n');
        }
        if (actions.exec) {
            me.doExec(actions.exec);
        }
    },
    doSwitch: function(action) {
        var me = this,
            key;
        
        if (action === '.' || action === '#') {
            key = action === '.' ? 'values' : 'xindex';
            me.body.push('switch (', key, ') {\n');
        } else if (me.propNameRe.test(action)) {
            me.body.push('switch (', me.parseTag(action), ') {\n');
        } else 
        {
            me.body.push('switch (', me.addFn(action), me.callFn, ') {\n');
        }
        me.switches.push(0);
    },
    doCase: function(action) {
        var me = this,
            cases = Ext.isArray(action) ? action : [
                action
            ],
            n = me.switches.length - 1,
            match, i;
        if (me.switches[n]) {
            me.body.push('break;\n');
        } else {
            me.switches[n]++;
        }
        for (i = 0 , n = cases.length; i < n; ++i) {
            match = me.intRe.exec(cases[i]);
            cases[i] = match ? match[1] : ("'" + cases[i].replace(me.aposRe, "\\'") + "'");
        }
        me.body.push('case ', cases.join(': case '), ':\n');
    },
    doDefault: function() {
        var me = this,
            n = me.switches.length - 1;
        if (me.switches[n]) {
            me.body.push('break;\n');
        } else {
            me.switches[n]++;
        }
        me.body.push('default:\n');
    },
    doEnd: function(type, actions) {
        var me = this,
            L = me.level - 1;
        if (type == 'for' || type == 'foreach') {
            
            if (actions.exec) {
                me.doExec(actions.exec);
            }
            me.body.push('}\n');
            me.body.push('parent=p', L, ';values=r', L + 1, ';xcount=n' + L + ';xindex=i', L, '+1;xkey=k', L, ';\n');
        } else if (type == 'if' || type == 'switch') {
            me.body.push('}\n');
        }
    },
    doFor: function(action, actions) {
        var me = this,
            s,
            L = me.level,
            up = L - 1,
            parentAssignment;
        
        if (action === '.') {
            s = 'values';
        } else if (me.propNameRe.test(action)) {
            s = me.parseTag(action);
        } else 
        {
            s = me.addFn(action) + me.callFn;
        }
        
        
        if (me.maxLevel < L) {
            me.maxLevel = L;
            me.body.push('var ');
        }
        if (action == '.') {
            parentAssignment = 'c' + L;
        } else {
            parentAssignment = 'a' + up + '?c' + up + '[i' + up + ']:c' + up;
        }
        me.body.push('i', L, '=0,n', L, '=0,c', L, '=', s, ',a', L, '=', me.createArrayTest(L), ',r', L, '=values,p', L, ',k', L, ';\n', 'p', L, '=parent=', parentAssignment, '\n', 'if (c', L, '){if(a', L, '){n', L, '=c', L, '.length;}else if (c', L, '.isMixedCollection){c', L, '=c', L, '.items;n', L, '=c', L, '.length;}else if(c', L, '.isStore){c', L, '=c', L, '.data.items;n', L, '=c', L, '.length;}else{c', L, '=[c', L, '];n', L, '=1;}}\n', 'for (xcount=n', L, ';i', L, '<n' + L + ';++i', L, '){\n', 'values=c', L, '[i', L, ']');
        if (actions.propName) {
            me.body.push('.', actions.propName);
        }
        me.body.push('\n', 'xindex=i', L, '+1\n');
        if (actions.between) {
            me.body.push('if(xindex>1){ out.push("', actions.between, '"); } \n');
        }
    },
    doForEach: function(action, actions) {
        var me = this,
            s,
            L = me.level,
            up = L - 1,
            parentAssignment;
        
        if (action === '.') {
            s = 'values';
        } else if (me.propNameRe.test(action)) {
            s = me.parseTag(action);
        } else 
        {
            s = me.addFn(action) + me.callFn;
        }
        
        
        if (me.maxLevel < L) {
            me.maxLevel = L;
            me.body.push('var ');
        }
        if (action == '.') {
            parentAssignment = 'c' + L;
        } else {
            parentAssignment = 'a' + up + '?c' + up + '[i' + up + ']:c' + up;
        }
        me.body.push('i', L, '=-1,n', L, '=0,c', L, '=', s, ',a', L, '=', me.createArrayTest(L), ',r', L, '=values,p', L, ',k', L, ';\n', 'p', L, '=parent=', parentAssignment, '\n', 'for(k', L, ' in c', L, '){\n', 'xindex=++i', L, '+1;\n', 'xkey=k', L, ';\n', 'values=c', L, '[k', L, '];');
        if (actions.propName) {
            me.body.push('.', actions.propName);
        }
        if (actions.between) {
            me.body.push('if(xindex>1){ out.push("', actions.between, '"); } \n');
        }
    },
    createArrayTest: ('isArray' in Array) ? function(L) {
        return 'Array.isArray(c' + L + ')';
    } : function(L) {
        return 'ts.call(c' + L + ')==="[object Array]"';
    },
    doExec: function(action, actions) {
        var me = this,
            name = 'f' + me.definitions.length,
            guards = me.guards[me.strict ? 0 : 1];
        me.definitions.push('function ' + name + '(' + me.fnArgs + ') {', guards.doTry, ' var $v = values; with($v) {', '  ' + action, ' }', guards.doCatch, '}');
        me.body.push(name + me.callFn + '\n');
    },
    
    
    guards: [
        {
            doTry: '',
            doCatch: ''
        },
        {
            doTry: 'try { ',
            doCatch: ' } catch(e) {\n' + 
            'Ext.log.warn("XTemplate evaluation exception: " + e.message);\n' + 
            '}'
        }
    ],
    addFn: function(body) {
        var me = this,
            name = 'f' + me.definitions.length,
            guards = me.guards[me.strict ? 0 : 1];
        if (body === '.') {
            me.definitions.push('function ' + name + '(' + me.fnArgs + ') {', ' return values', '}');
        } else if (body === '..') {
            me.definitions.push('function ' + name + '(' + me.fnArgs + ') {', ' return parent', '}');
        } else {
            me.definitions.push('function ' + name + '(' + me.fnArgs + ') {', guards.doTry, ' var $v = values; with($v) {', '  return(' + body + ')', ' }', guards.doCatch, '}');
        }
        return name;
    },
    parseTag: function(tag) {
        var me = this,
            m = me.tagRe.exec(tag),
            name, format, args, math, v;
        if (!m) {
            return null;
        }
        name = m[1];
        format = m[2];
        args = m[3];
        math = m[4];
        
        if (name == '.') {
            
            if (!me.validTypes) {
                me.definitions.push('var validTypes={string:1,number:1,boolean:1};');
                me.validTypes = true;
            }
            v = 'validTypes[typeof values] || ts.call(values) === "[object Date]" ? values : ""';
        }
        
        else if (name == '#') {
            v = 'xindex';
        }
        
        else if (name == '$') {
            v = 'xkey';
        } else if (name.substr(0, 7) == "parent.") {
            v = name;
        }
        
        else if (isNaN(name) && name.indexOf('-') == -1 && name.indexOf('.') != -1) {
            v = "values." + name;
        } else 
        
        {
            v = "values['" + name + "']";
        }
        if (math) {
            v = '(' + v + math + ')';
        }
        if (format && me.useFormat) {
            args = args ? ',' + args : "";
            if (format.substr(0, 5) != "this.") {
                format = "fm." + format + '(';
            } else {
                format += '(';
            }
        } else {
            return v;
        }
        return format + v + args + ')';
    },
    
    evalTpl: function($) {
        
        
        
        
        eval($);
        return $;
    },
    newLineRe: /\r\n|\r|\n/g,
    aposRe: /[']/g,
    intRe: /^\s*(\d+)\s*$/,
    tagRe: /^([\w-\.\#\$]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\/]\s?[\d\.\+\-\*\/\(\)]+)?$/
}, function() {
    var proto = this.prototype;
    proto.fnArgs = 'out,values,parent,xindex,xcount,xkey';
    proto.callFn = '.call(this,' + proto.fnArgs + ')';
});


Ext.define('Ext.XTemplate', {
    extend: Ext.Template,
    isXTemplate: true,
    
    emptyObj: {},
    
    
    
    fn: null,
    
    strict: false,
    apply: function(values, parent, xindex, xcount) {
        return this.applyOut(values, [], parent, xindex, xcount).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: {
        
        getTpl: function(instance, name) {
            var tpl = instance[name],
                
                owner;
            if (tpl && !tpl.isTemplate) {
                
                
                tpl = Ext.ClassManager.dynInstantiate('Ext.XTemplate', tpl);
                
                if (instance.hasOwnProperty(name)) {
                    
                    owner = instance;
                } else {
                    
                    for (owner = instance.self.prototype; owner && !owner.hasOwnProperty(name); owner = owner.superclass) {}
                }
                owner[name] = tpl;
                tpl.owner = owner;
            }
            
            
            return tpl || null;
        }
    }
});


Ext.define('Ext.app.EventDomain', {
    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 ret = doFireEvent.apply(this, arguments);
            if (ret !== false && !this.isSuspended(ev)) {
                ret = domain.dispatch(this, ev, args);
            }
            return ret;
        };
    },
    
    unlisten: function(controllerId) {
        var bus = this.bus,
            id = controllerId,
            monitoredClasses = this.monitoredClasses,
            monitoredClassesCount = monitoredClasses.length,
            controllers, ev, events, len, item, selector, selectors, i, j, info, classHasListeners;
        if (controllerId.isController) {
            id = controllerId.getId();
        }
        for (ev in bus) {
            ev = Ext.canonicalEventName(ev);
            if (bus.hasOwnProperty(ev) && (selectors = bus[ev])) {
                for (selector in selectors) {
                    controllers = selectors[selector];
                    info = controllers[id];
                    if (info) {
                        events = info.list;
                        if (events) {
                            for (i = 0 , len = events.length; i < len; ++i) {
                                item = events[i];
                                item.clearListeners();
                                for (j = 0; j < monitoredClassesCount; ++j) {
                                    classHasListeners = monitoredClasses[j].hasListeners;
                                    if (classHasListeners) {
                                        
                                        
                                        classHasListeners._decr_(item.name);
                                    }
                                }
                            }
                            delete controllers[id];
                        }
                    }
                }
            }
        }
    },
    destroy: function() {
        this.monitoredClasses = this.bus = null;
        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(arguments);
    },
    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.app.BaseController', {
    mixins: [
        Ext.mixin.Observable
    ],
    isController: true,
    config: {
        
        id: null,
        
        control: null,
        
        listen: null,
        
        routes: null,
        before: null
    },
    
    constructor: function(config) {
        var me = this;
        
        
        
        
        
        Ext.apply(me, config);
        
        
        delete me.control;
        delete me.listen;
        me.eventbus = Ext.app.EventBus;
        
        me.mixins.observable.constructor.call(me, config);
        
        me.ensureId();
    },
    applyListen: function(listen) {
        if (Ext.isObject(listen)) {
            listen = Ext.clone(listen);
        }
        return listen;
    },
    applyControl: function(control) {
        if (Ext.isObject(control)) {
            control = Ext.clone(control);
        }
        return control;
    },
    
    updateControl: function(control) {
        this.ensureId();
        if (control) {
            this.control(control);
        }
    },
    
    updateListen: function(listen) {
        this.ensureId();
        if (listen) {
            this.listen(listen);
        }
    },
    
    updateRoutes: function(routes) {
        if (routes) {
            var me = this,
                befores = me.getBefore() || {},
                Router = Ext.app.route.Router,
                url, config, method;
            for (url in routes) {
                config = routes[url];
                if (Ext.isString(config)) {
                    config = {
                        action: config
                    };
                }
                method = config.action;
                if (!config.before) {
                    config.before = befores[method];
                }
                
                else if (befores[method]) {
                    Ext.log.warn('You have a before method configured on a route ("' + url + '") and in the before object property also in the "' + me.self.getName() + '" controller. Will use the before method in the route and disregard the one in the before property.');
                }
                
                
                Router.connect(url, config, me);
            }
        }
    },
    isActive: function() {
        return true;
    },
    
    control: function(selectors, listeners, controller) {
        var me = this,
            ctrl = controller,
            obj;
        if (Ext.isString(selectors)) {
            obj = {};
            obj[selectors] = listeners;
        } else {
            obj = selectors;
            ctrl = listeners;
        }
        me.eventbus.control(obj, ctrl || me);
    },
    
    listen: function(to, controller) {
        this.eventbus.listen(to, controller || this);
    },
    destroy: function() {
        var me = this,
            bus = me.eventbus;
        Ext.app.route.Router.disconnectAll(me);
        if (bus) {
            bus.unlisten(me);
            me.eventbus = null;
        }
        me.callParent();
    },
    
    redirectTo: function(token, force) {
        if (token.isModel) {
            token = token.toUrl();
        }
        if (!force) {
            var currentToken = Ext.util.History.getToken();
            if (currentToken === token) {
                return false;
            }
        } else {
            Ext.app.route.Router.onStateChange(token);
        }
        Ext.util.History.add(token);
        return true;
    }
});


Ext.define('Ext.app.Util', {}, function() {
    Ext.apply(Ext.app, {
        namespaces: {
            Ext: {}
        },
        
        addNamespaces: function(namespace) {
            var namespaces = Ext.app.namespaces,
                i, l;
            if (!Ext.isArray(namespace)) {
                namespace = [
                    namespace
                ];
            }
            for (i = 0 , l = namespace.length; i < l; i++) {
                namespaces[namespace[i]] = true;
            }
        },
        
        clearNamespaces: function() {
            Ext.app.namespaces = {};
        },
        
        getNamespace: function(className) {
            var namespaces = Ext.apply({}, Ext.ClassManager.paths, Ext.app.namespaces),
                deepestPrefix = '',
                prefix;
            for (prefix in namespaces) {
                if (namespaces.hasOwnProperty(prefix) && prefix.length > deepestPrefix.length && (prefix + '.' === className.substring(0, prefix.length + 1))) {
                    deepestPrefix = prefix;
                }
            }
            return deepestPrefix === '' ? undefined : deepestPrefix;
        },
        
        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,
    statics: {
        
        createFilterFn: function(filters) {
            if (!filters) {
                return Ext.returnTrue;
            }
            return function(candidate) {
                var items = filters.isCollection ? filters.items : filters,
                    length = items.length,
                    match = true,
                    i, filter;
                for (i = 0; match && i < length; i++) {
                    filter = items[i];
                    
                    if (!filter.getDisabled()) {
                        match = filter.filter(candidate);
                    }
                }
                return match;
            };
        },
        
        isInvalid: function(cfg) {
            if (!cfg.filterFn) {
                
                if (!cfg.property) {
                    return 'A Filter requires either a property or a filterFn to be set';
                }
                if (!cfg.hasOwnProperty('value') && !cfg.operator) {
                    return 'A Filter requires either a property and value, or a filterFn to be set';
                }
            }
            return false;
        }
    },
    
    constructor: function(config) {
        
        var warn = Ext.util.Filter.isInvalid(config);
        if (warn) {
            Ext.log.warn(warn);
        }
        
        this.initConfig(config);
    },
    preventConvert: {
        'in': 1,
        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;
        }
        return filterFn;
    },
    
    createRegexFilter: function() {
        var me = this,
            anyMatch = !!me.getAnyMatch(),
            exact = !!me.getExactMatch(),
            value = me.getValue(),
            matcher = Ext.String.createRegex(value, !anyMatch, 
            !anyMatch && exact, 
            !me.getCaseSensitive());
        return function(item) {
            var val = me.getPropertyValue(item);
            return matcher ? matcher.test(val) : (val == null);
        };
    },
    
    getPropertyValue: function(item) {
        var root = this._root,
            value = (root == null) ? item : item[root];
        return value[this._property];
    },
    
    getState: function() {
        var config = this.getInitialConfig(),
            result = {},
            name;
        for (name in config) {
            
            
            if (config.hasOwnProperty(name)) {
                result[name] = config[name];
            }
        }
        delete result.root;
        result.value = this.getValue();
        return result;
    },
    getScope: function() {
        return this.scope;
    },
    
    serialize: function() {
        var result = this.getState(),
            serializer = this.getSerializer();
        delete result.id;
        delete result.serializer;
        if (serializer) {
            serializer.call(this, result);
        }
        return result;
    },
    updateOperator: function() {
        this._filterFn = null;
    },
    updateValue: function(value) {
        this._filterFn = null;
        if (this.getDisableOnEmpty()) {
            this.setDisabled(Ext.isEmpty(value));
        }
    },
    updateDisableOnEmpty: function(disableOnEmpty) {
        var disabled = false;
        if (disableOnEmpty) {
            disabled = Ext.isEmpty(this.getValue());
        }
        this.setDisabled(disabled);
    },
    privates: {
        getCandidateValue: function(candidate, v, preventCoerce) {
            var me = this,
                convert = me._convert,
                result = me.getPropertyValue(candidate);
            if (convert) {
                result = convert.call(me.scope || me, result);
            } else if (!preventCoerce) {
                result = Ext.coerce(result, v);
            }
            return result;
        }
    }
}, function() {
    var prototype = this.prototype,
        operatorFns = (prototype.operatorFns = {
            "<": function(candidate) {
                var v = this._filterValue;
                return this.getCandidateValue(candidate, v) < v;
            },
            "<=": function(candidate) {
                var v = this._filterValue;
                return this.getCandidateValue(candidate, v) <= v;
            },
            "=": function(candidate) {
                var me = this,
                    v = me._filterValue;
                candidate = me.getCandidateValue(candidate, v);
                if (me.isDateValue && candidate instanceof Date) {
                    candidate = candidate.getTime();
                    v = me.dateValue;
                }
                return candidate == v;
            },
            "===": function(candidate) {
                var me = this,
                    v = me._filterValue;
                candidate = me.getCandidateValue(candidate, v, true);
                if (me.isDateValue && candidate instanceof Date) {
                    candidate = candidate.getTime();
                    v = me.dateValue;
                }
                return candidate === v;
            },
            ">=": function(candidate) {
                var v = this._filterValue;
                return this.getCandidateValue(candidate, v) >= v;
            },
            ">": function(candidate) {
                var v = this._filterValue;
                return this.getCandidateValue(candidate, v) > v;
            },
            "!=": function(candidate) {
                var me = this,
                    v = me._filterValue;
                candidate = me.getCandidateValue(candidate, v);
                if (me.isDateValue && candidate instanceof Date) {
                    candidate = candidate.getTime();
                    v = me.dateValue;
                }
                return candidate != v;
            },
            "!==": function(candidate) {
                var me = this,
                    v = me._filterValue;
                candidate = me.getCandidateValue(candidate, v, true);
                if (me.isDateValue && candidate instanceof Date) {
                    candidate = candidate.getTime();
                    v = me.dateValue;
                }
                return candidate !== v;
            },
            "in": function(candidate) {
                var v = this._filterValue;
                return Ext.Array.contains(v, this.getCandidateValue(candidate, v));
            },
            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;
            }
        });
    
    operatorFns['=='] = operatorFns['='];
    operatorFns.gt = operatorFns['>'];
    operatorFns.ge = operatorFns['>='];
    operatorFns.lt = operatorFns['<'];
    operatorFns.le = operatorFns['<='];
    operatorFns.eq = operatorFns['='];
    operatorFns.ne = operatorFns['!='];
});


Ext.define('Ext.util.Observable', {
    extend: Ext.mixin.Observable,
    
    
    $applyConfigs: true
}, 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.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(o) {
        return o.id;
    },
    
    replace: function(key, o) {
        var me = this,
            old, index;
        if (arguments.length == 1) {
            o = arguments[0];
            key = me.getKey(o);
        }
        old = me.map[key];
        if (typeof key == 'undefined' || key === null || typeof old == 'undefined') {
            return me.add(key, o);
        }
        me.generation++;
        index = me.indexOfKey(key);
        me.items[index] = o;
        me.map[key] = o;
        if (me.hasListeners.replace) {
            me.fireEvent('replace', key, old, o);
        }
        return o;
    },
    
    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 items.$extCollectionIndex;
        }
        me.generation++;
        me.indexGeneration = me.generation;
        me.fireEvent('sort', me, items, keys);
    },
    
    findInsertionIndex: function(newItem, sorterFn) {
        var me = this,
            items = me.items,
            start = 0,
            end = items.length - 1,
            middle, comparison;
        if (!sorterFn) {
            sorterFn = me.generateComparator();
        }
        while (start <= end) {
            middle = (start + end) >> 1;
            comparison = sorterFn(newItem, items[middle]);
            if (comparison >= 0) {
                start = middle + 1;
            } else if (comparison < 0) {
                end = middle - 1;
            }
        }
        return start;
    },
    
    reorder: function(mapping) {
        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) {
        collection.addObserver(this);
    },
    clone: function() {
        return new Ext.util.CollectionKey(this.getCurrentConfig());
    }
});


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,
        
        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,
    
    
    
    
    
    
    
    
    
    
    constructor: function(config) {
        var me = this;
        
        me.items = [];
        
        me.map = {};
        
        me.length = 0;
        
        if (config && config.keyFn) {
            me.getKey = config.keyFn;
        }
        me.mixins.observable.constructor.call(me, config);
    },
    
    destroy: function() {
        var me = this,
            filters = me._filters,
            sorters = me._sorters,
            groups = me._groups;
        if (filters) {
            filters.destroy();
            me._filters = null;
        }
        if (sorters) {
            sorters.destroy();
            me._sorters = null;
        }
        if (groups) {
            groups.destroy();
            me._groups = null;
        }
        me.setSource(null);
        me.observers = me.items = me.map = null;
        me.callParent();
    },
    
    add: function(item) {
        var me = this,
            items = me.decodeItems(arguments, 0),
            ret = items;
        if (items.length) {
            me.requestedIndex = me.length;
            me.splice(me.length, 0, items);
            delete me.requestedIndex;
            ret = (items.length === 1) ? items[0] : items;
        }
        return ret;
    },
    
    replaceAll: function() {
        var me = this,
            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 = [];
            me.length = 0;
            me.map = {};
            me.indices = {};
            me.generation++;
            
            extraKeys = me.getExtraKeys();
            if (extraKeys) {
                for (indexName in extraKeys) {
                    extraKeys[indexName].clear();
                }
            }
        }
        return ret;
    },
    
    clone: function() {
        var me = this,
            copy = new me.self(me.initialConfig);
        copy.add(me.items);
        return copy;
    },
    
    collect: function(property, root, allowNull) {
        var items = this.items,
            length = items.length,
            map = {},
            ret = [],
            i, strValue, value;
        for (i = 0; i < length; ++i) {
            value = items[i];
            value = (root ? value[root] : value)[property];
            strValue = String(value);
            if ((allowNull || !Ext.isEmpty(value)) && !map[strValue]) {
                map[strValue] = 1;
                ret.push(value);
            }
        }
        return ret;
    },
    
    contains: function(item) {
        var ret = false,
            key;
        if (item != null) {
            key = this.getKey(item);
            ret = this.map[key] === item;
        }
        return ret;
    },
    
    containsKey: function(key) {
        return key in this.map;
    },
    
    createFiltered: function(property, value, anyMatch, caseSensitive, exactMatch) {
        var me = this,
            ret = new me.self(me.initialConfig),
            root = me.getRootProperty(),
            items = me.items,
            length, i, filters, fn, scope;
        if (Ext.isFunction(property)) {
            fn = property;
            scope = value;
        } else {
            
            if (Ext.isString(property)) {
                filters = [
                    new Ext.util.Filter({
                        property: property,
                        value: value,
                        root: root,
                        anyMatch: anyMatch,
                        caseSensitive: caseSensitive,
                        exactMatch: exactMatch
                    })
                ];
            } else if (property instanceof Ext.util.Filter) {
                filters = [
                    property
                ];
                property.setRoot(root);
            } else if (Ext.isArray(property)) {
                filters = property.slice(0);
                for (i = 0 , length = filters.length; i < length; ++i) {
                    filters[i].setRoot(root);
                }
            }
            
            
            
            fn = Ext.util.Filter.createFilterFn(filters);
        }
        scope = scope || me;
        for (i = 0 , length = items.length; i < length; i++) {
            if (fn.call(scope, items[i])) {
                ret.add(items[i]);
            }
        }
        return ret;
    },
    
    filterBy: function(fn, scope) {
        return this.createFiltered(fn, scope);
    },
    
    each: function(fn, scope) {
        var items = this.items,
            len = items.length,
            i, ret;
        if (len) {
            scope = scope || this;
            items = items.slice(0);
            
            for (i = 0; i < len; i++) {
                ret = fn.call(scope, items[i], i, len);
                if (ret === false) {
                    break;
                }
            }
        }
        return ret;
    },
    
    eachKey: function(fn, scope) {
        var me = this,
            items = me.items,
            len = items.length,
            i, item, key, ret;
        if (len) {
            scope = scope || me;
            items = items.slice(0);
            
            for (i = 0; i < len; i++) {
                key = me.getKey(item = items[i]);
                ret = fn.call(scope, key, item, i, len);
                if (ret === false) {
                    break;
                }
            }
        }
        return ret;
    },
    
    endUpdate: function() {
        if (!--this.updating) {
            this.notify('endupdate');
        }
    },
    
    find: function(property, value, start, startsWith, endsWith, ignoreCase) {
        if (Ext.isEmpty(value, false)) {
            return null;
        }
        var regex = Ext.String.createRegex(value, startsWith, endsWith, ignoreCase),
            root = this.getRootProperty();
        return this.findBy(function(item) {
            return item && regex.test((root ? item[root] : item)[property]);
        }, null, start);
    },
    
    findBy: function(fn, scope, start) {
        var me = this,
            items = me.items,
            len = items.length,
            i, item, key;
        scope = scope || me;
        for (i = start || 0; i < len; i++) {
            key = me.getKey(item = items[i]);
            if (fn.call(scope, item, key)) {
                return items[i];
            }
        }
        return null;
    },
    
    findIndex: function(property, value, start, startsWith, endsWith, ignoreCase) {
        var item = this.find(property, value, start, startsWith, endsWith, ignoreCase);
        return item ? this.indexOf(item) : -1;
    },
    
    findIndexBy: function(fn, scope, start) {
        var item = this.findBy(fn, scope, start);
        return item ? this.indexOf(item) : -1;
    },
    
    first: function(grouped) {
        var groups = grouped ? this.getGroups() : undefined;
        return groups ? this.aggregateGroups(groups, null, 'first') : this.items[0];
    },
    
    last: function(grouped) {
        var groups = grouped ? this.getGroups() : undefined;
        return groups ? this.aggregateGroups(groups, null, 'last') : this.items[this.length - 1];
    },
    
    get: function(key) {
        return this.map[key];
    },
    
    getAt: function(index) {
        return this.items[index];
    },
    
    getByKey: function(key) {
        return this.map[key];
    },
    
    getCount: function() {
        return this.length;
    },
    
    getKey: function(item) {
        var id = item.id;
        return (id === 0 || id) ? id : ((id = item._id) === 0 || id) ? id : item.getId();
    },
    
    getRange: function(begin, end) {
        var items = this.items,
            length = items.length,
            range;
        
        if (begin > end) {
            Ext.raise('Inverted range passed to Collection.getRange: [' + begin + ',' + end + ']');
        }
        
        if (!length) {
            range = [];
        } else {
            range = Ext.Number.clipIndices(length, [
                begin,
                end
            ]);
            range = items.slice(range[0], range[1]);
        }
        return range;
    },
    
    
    getValues: function(property, root, start, end) {
        var items = this.items,
            range = Ext.Number.clipIndices(items.length, [
                start,
                end
            ]),
            ret = [],
            i, value;
        for (i = range[0] , end = range[1]; i < end; ++i) {
            value = items[i];
            value = (root ? value[root] : value)[property];
            ret.push(value);
        }
        return ret;
    },
    
    indexOf: function(item) {
        if (!item) {
            return -1;
        }
        var key = this.getKey(item);
        return this.indexOfKey(key);
    },
    
    indexOfKey: function(key) {
        var me = this,
            indices = me.indices;
        if (key in me.map) {
            if (!indices) {
                indices = me.getIndices();
            }
            return indices[key];
        }
        return -1;
    },
    
    insert: function(index, item) {
        var me = this,
            items = me.decodeItems(arguments, 1),
            ret = items;
        if (items.length) {
            me.requestedIndex = index;
            me.splice(index, 0, items);
            delete me.requestedIndex;
            ret = (items.length === 1) ? items[0] : items;
        }
        return ret;
    },
    
    itemChanged: function(item, modified, oldKey, 
    meta) {
        var me = this,
            keyChanged = oldKey === 0 || !!oldKey,
            filtered = me.filtered && me.getAutoFilter(),
            filterChanged = false,
            itemMovement = 0,
            items = me.items,
            last = me.length - 1,
            sorted = me.sorted && last > 0 && me.getAutoSort(),
            
            
            
            
            source = me.getSource(),
            toRemove = 0,
            itemFiltered = false,
            wasFiltered = false,
            details, newKey, sortFn, toAdd, index, newIndex;
        
        if (source && !source.updating) {
            source.itemChanged(item, modified, oldKey, meta);
        } else 
        
        {
            newKey = me.getKey(item);
            if (filtered) {
                index = me.indexOfKey(keyChanged ? oldKey : newKey);
                wasFiltered = (index < 0);
                itemFiltered = me.isItemFiltered(item);
                filterChanged = (wasFiltered !== itemFiltered);
            }
            if (filterChanged) {
                if (itemFiltered) {
                    toRemove = [
                        item
                    ];
                    newIndex = -1;
                } else {
                    toAdd = [
                        item
                    ];
                    newIndex = me.length;
                }
            }
            
            
            
            else if (sorted && !itemFiltered) {
                
                
                if (!filtered) {
                    
                    
                    index = me.indexOfKey(keyChanged ? oldKey : newKey);
                }
                sortFn = me.getSortFn();
                if (index !== -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.beginUpdate();
            me.notify('beforeitemchange', [
                details
            ]);
            if (keyChanged) {
                me.updateKey(item, oldKey);
            }
            if (toAdd || toRemove) {
                
                
                
                
                
                me.splice(newIndex, toRemove, toAdd);
            }
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            if (itemMovement > 0) {
                details.newIndex--;
            } else if (itemMovement < 0) {
                details.oldIndex++;
            }
            
            
            
            me.notify(itemFiltered ? 'filtereditemchange' : 'itemchange', [
                details
            ]);
            me.endUpdate();
        }
    },
    
    remove: function(item) {
        var me = this,
            items = me.decodeRemoveItems(arguments, 0),
            length = me.length;
        me.splice(0, items);
        return length - me.length;
    },
    
    removeAll: function() {
        var me = this,
            length = me.length;
        if (me.generation && length) {
            me.splice(0, length);
        }
        return me;
    },
    
    removeAt: function(index, count) {
        var me = this,
            length = me.length,
            Num = Ext.Number,
            range = Num.clipIndices(length, [
                index,
                (count === undefined) ? 1 : count
            ], Num.Clip.COUNT),
            n = range[0],
            removeCount = range[1] - n,
            item = (removeCount === 1) && me.getAt(n),
            removed;
        me.splice(n, removeCount);
        removed = me.length - length;
        return (item && removed) ? item : removed;
    },
    
    removeByKey: function(key) {
        var item = this.getByKey(key);
        if (!item || !this.remove(item)) {
            return false;
        }
        return item;
    },
    
    replace: function(item) {
        var index = this.indexOf(item);
        if (index === -1) {
            this.add(item);
        } else {
            this.insert(index, item);
        }
    },
    
    splice: function(index, toRemove, toAdd) {
        var me = this,
            autoSort = me.sorted && me.getAutoSort(),
            map = me.map,
            items = me.items,
            length = me.length,
            removeItems = (toRemove instanceof Array) ? me.decodeRemoveItems(toRemove) : null,
            isRemoveCount = !removeItems,
            Num = Ext.Number,
            range = Num.clipIndices(length, [
                index,
                isRemoveCount ? toRemove : 0
            ], Num.Clip.COUNT),
            begin = range[0],
            end = range[1],
            
            removeCount = end - begin,
            newItems = me.decodeItems(arguments, 2),
            newCount = newItems ? newItems.length : 0,
            addItems, newItemsMap, removeMap,
            insertAt = begin,
            indices = me.indices || ((newCount || removeItems) ? me.getIndices() : null),
            adds = null,
            removes = removeCount ? [
                begin
            ] : null,
            newKeys = null,
            source = me.getSource(),
            chunk, chunkItems, chunks, i, item, itemIndex, k, key, keys, n, duplicates, sorters, end;
        if (source && !source.updating) {
            
            
            
            
            if (isRemoveCount) {
                removeItems = [];
                for (i = 0; i < removeCount; ++i) {
                    removeItems.push(items[begin + i]);
                }
            }
            if (begin < length) {
                
                
                i = source.indexOf(items[begin]);
            } else {
                
                i = source.length;
            }
            source.splice(i, removeItems, newItems);
            return me;
        }
        
        
        
        
        if (newCount) {
            addItems = newItems;
            newKeys = [];
            newItemsMap = {};
            
            
            
            
            if (autoSort) {
                
                sorters = me.getSorters();
                if (newCount > 1) {
                    if (!addItems.$cloned) {
                        newItems = addItems = addItems.slice(0);
                    }
                    me.sortData(addItems);
                }
            }
            for (i = 0; i < newCount; ++i) {
                key = me.getKey(item = newItems[i]);
                if ((k = newItemsMap[key]) !== undefined) {
                    
                    
                    
                    (duplicates || (duplicates = {}))[k] = 1;
                } else {
                    
                    
                    
                    itemIndex = indices[key];
                    if (itemIndex < begin || end <= itemIndex) {
                        (removes || (removes = [])).push(itemIndex);
                    }
                }
                
                newItemsMap[key] = i;
                
                newKeys.push(key);
            }
            
            if (duplicates) {
                keys = newKeys;
                addItems = [];
                newKeys = [];
                addItems.$cloned = true;
                for (i = 0; i < newCount; ++i) {
                    if (!duplicates[i]) {
                        item = newItems[i];
                        addItems.push(item);
                        newKeys.push(keys[i]);
                    }
                }
                newCount = addItems.length;
            }
            adds = {
                
                
                
                items: addItems,
                keys: newKeys
            };
        }
        
        for (i = removeItems ? removeItems.length : 0; i-- > 0; ) {
            key = me.getKey(removeItems[i]);
            if ((itemIndex = indices[key]) !== undefined) {
                
                (removes || (removes = [])).push(itemIndex);
            }
        }
        
        if (!adds && !removes) {
            return me;
        }
        me.beginUpdate();
        
        
        
        if (removes) {
            chunk = null;
            chunks = [];
            removeMap = {};
            if (removes.length > 1) {
                removes.sort(Ext.Array.numericSortFn);
            }
            
            
            for (i = 0 , n = removes.length; i < n; ++i) {
                key = me.getKey(item = items[itemIndex = removes[i]]);
                if (!(key in map)) {
                    
                    continue;
                }
                
                
                delete map[key];
                
                
                
                
                
                
                
                
                
                if (!chunk || itemIndex > (chunk.at + chunkItems.length)) {
                    chunks.push(chunk = {
                        at: itemIndex,
                        items: (chunkItems = []),
                        keys: (keys = []),
                        map: removeMap,
                        next: chunk,
                        replacement: adds
                    });
                    
                    if (adds) {
                        adds.replaced = chunk;
                    }
                }
                chunkItems.push(removeMap[key] = item);
                keys.push(key);
                if (itemIndex < insertAt) {
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    --insertAt;
                }
                if (removeCount > 1 && itemIndex === begin) {
                    
                    
                    
                    
                    
                    
                    --removeCount;
                    
                    removes[i--] = ++begin;
                }
            }
            
            
            if (adds) {
                adds.at = insertAt;
            }
            
            
            
            for (k = chunks.length; k-- > 0; ) {
                chunk = chunks[k];
                i = chunk.at;
                n = chunk.items.length;
                if (i + n < length) {
                    
                    
                    
                    me.indices = indices = null;
                }
                me.length = length -= n;
                
                
                
                
                items.splice(i, n);
                if (indices) {
                    keys = chunk.keys;
                    for (i = 0; i < n; ++i) {
                        delete indices[keys[i]];
                    }
                }
                ++me.generation;
                me.notify('remove', [
                    chunk
                ]);
            }
        }
        
        if (adds) {
            if (autoSort && newCount > 1 && length) {
                me.spliceMerge(addItems, newKeys);
            } else {
                if (autoSort) {
                    if (newCount > 1) {
                        
                        insertAt = 0;
                        me.indices = indices = null;
                    } else {
                        
                        
                        insertAt = sorters.findInsertionIndex(adds.items[0], items, me.getSortFn());
                    }
                }
                if (insertAt === length) {
                    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) {
        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', [
                    {
                        item: item,
                        newKey: newKey,
                        oldKey: oldKey
                    }
                ]);
                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;
    },
    
    onCollectionBeginUpdate: function() {
        this.beginUpdate();
    },
    
    onCollectionEndUpdate: function() {
        this.endUpdate();
    },
    
    onCollectionItemChange: function(source, details) {
        
        delete this.onCollectionUpdateKey;
        this.itemChanged(details.item, details.modified, details.oldKey, details.meta);
    },
    
    
    onCollectionFilteredItemChange: null,
    
    onCollectionRefresh: function(source) {
        var me = this,
            map = {},
            indices = {},
            i, item, items, key, length;
        items = source.items;
        items = me.filtered && me.getAutoFilter() ? Ext.Array.filter(items, me.getFilterFn()) : items.slice(0);
        if (me.sorted) {
            me.sortData(items);
        }
        me.items = items;
        me.length = length = items.length;
        me.map = map;
        me.indices = indices;
        for (i = 0; i < length; ++i) {
            key = me.getKey(item = items[i]);
            map[key] = item;
            indices[key] = i;
        }
        me.notify('refresh');
    },
    
    onCollectionRemove: function(source, details) {
        this.splice(0, details.items);
    },
    
    
    
    
    
    onCollectionUpdateKey: function(source, details) {
        this.updateKey(details.item, details.oldKey);
    },
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    _aggregators: {
        average: function(items, begin, end, property, root) {
            var n = end - begin;
            return n && this._aggregators.sum.call(this, items, begin, end, property, root) / n;
        },
        bounds: function(items, begin, end, property, root) {
            for (var value, max, min,
                i = begin; i < end; ++i) {
                value = items[i];
                value = (root ? value[root] : value)[property];
                
                
                
                if (!(value < max)) {
                    
                    max = value;
                }
                if (!(value > min)) {
                    
                    min = value;
                }
            }
            return [
                min,
                max
            ];
        },
        count: function(items) {
            return items.length;
        },
        extremes: function(items, begin, end, property, root) {
            var most = null,
                least = null,
                i, item, max, min, value;
            for (i = begin; i < end; ++i) {
                item = items[i];
                value = (root ? item[root] : item)[property];
                
                if (!(value < max)) {
                    
                    max = value;
                    most = item;
                }
                if (!(value > min)) {
                    
                    min = value;
                    least = item;
                }
            }
            return [
                least,
                most
            ];
        },
        max: function(items, begin, end, property, root) {
            var b = this._aggregators.bounds.call(this, items, begin, end, property, root);
            return b[1];
        },
        maxItem: function(items, begin, end, property, root) {
            var b = this._aggregators.extremes.call(this, items, begin, end, property, root);
            return b[1];
        },
        min: function(items, begin, end, property, root) {
            var b = this._aggregators.bounds.call(this, items, begin, end, property, root);
            return b[0];
        },
        minItem: function(items, begin, end, property, root) {
            var b = this._aggregators.extremes.call(this, items, begin, end, property, root);
            return b[0];
        },
        sum: function(items, begin, end, property, root) {
            for (var value,
                sum = 0,
                i = begin; i < end; ++i) {
                value = items[i];
                value = (root ? value[root] : value)[property];
                sum += value;
            }
            return sum;
        }
    },
    _eventToMethodMap: {
        add: 'onCollectionAdd',
        beforeitemchange: 'onCollectionBeforeItemChange',
        beginupdate: 'onCollectionBeginUpdate',
        endupdate: 'onCollectionEndUpdate',
        itemchange: 'onCollectionItemChange',
        filtereditemchange: 'onCollectionFilteredItemChange',
        refresh: 'onCollectionRefresh',
        remove: 'onCollectionRemove',
        beforesort: 'beforeCollectionSort',
        sort: 'onCollectionSort',
        filter: 'onCollectionFilter',
        filteradd: 'onCollectionFilterAdd',
        updatekey: 'onCollectionUpdateKey'
    },
    
    addObserver: function(observer) {
        var me = this,
            observers = me.observers;
        if (!observers) {
            me.observers = observers = [];
        }
        
        if (Ext.Array.contains(observers, observer)) {
            Ext.Error.raise('Observer already added');
        }
        
        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) {
            for (index = 0 , length = observers.length; index < length; ++index) {
                method = (observer = observers[index])[methodName];
                if (method) {
                    if (!added++) {
                        
                        args.unshift(me);
                    }
                    
                    method.apply(observer, args);
                }
            }
        }
        
        if (!me.hasListeners) {
            return;
        }
        if (me.hasListeners[eventName]) {
            if (!added) {
                args.unshift(me);
            }
            
            me.fireEventArgs(eventName, args);
        }
    },
    
    getFilterFn: function() {
        return this.getFilters().getFilterFn();
    },
    
    getFilters: function(autoCreate) {
        var ret = this._filters;
        if (!ret && autoCreate !== false) {
            ret = new Ext.util.FilterCollection();
            this.setFilters(ret);
        }
        return ret;
    },
    
    isItemFiltered: function(item) {
        return !this.getFilters().filterFn(item);
    },
    
    onFilterChange: function(filters) {
        var me = this,
            source = me.getSource(),
            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.length || me.length) {
            
            me.onCollectionRefresh(source);
        }
        me.notify('filter');
    },
    
    
    applyFilters: function(filters, collection) {
        if (filters == null || (filters && filters.isFilterCollection)) {
            return filters;
        }
        if (filters) {
            if (!collection) {
                collection = this.getFilters();
            }
            collection.splice(0, collection.length, filters);
        }
        return collection;
    },
    updateFilters: function(newFilters, oldFilters) {
        var me = this;
        if (oldFilters) {
            
            
            
            
            oldFilters.un('endupdate', 'onEndUpdateFilters', me);
        }
        if (newFilters) {
            newFilters.on({
                endupdate: 'onEndUpdateFilters',
                scope: me,
                priority: me.$endUpdatePriority
            });
            newFilters.$filterable = me;
        }
        me.onEndUpdateFilters(newFilters);
    },
    onEndUpdateFilters: function(filters) {
        var me = this,
            was = me.filtered,
            is = !!filters && (filters.length > 0);
        
        if (was || is) {
            me.filtered = is;
            me.onFilterChange(filters);
        }
    },
    
    getSortFn: function() {
        return this._sortFn || this.createSortFn();
    },
    
    getSorters: function(autoCreate) {
        var ret = this._sorters;
        if (!ret && autoCreate !== false) {
            ret = new Ext.util.SorterCollection();
            this.setSorters(ret);
        }
        return ret;
    },
    
    onSortChange: function() {
        if (this.sorted) {
            this.sortItems();
        }
    },
    
    sort: function(property, direction, mode) {
        var sorters = this.getSorters();
        sorters.addSort.apply(sorters, arguments);
        return this;
    },
    
    sortData: function(data) {
        Ext.Array.sort(data, this.getSortFn());
        return data;
    },
    
    sortItems: function(sortFn) {
        var me = this;
        if (me.sorted) {
            
            if (sortFn) {
                Ext.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) {
        if (!items) {
            items = this.items;
        }
        if (!comparatorFn) {
            comparatorFn = this.getSortFn();
        }
        return Ext.Array.binarySearch(items, item, comparatorFn);
    },
    applySorters: function(sorters, collection) {
        if (sorters == null || (sorters && sorters.isSorterCollection)) {
            return sorters;
        }
        if (sorters) {
            if (!collection) {
                collection = this.getSorters();
            }
            collection.splice(0, collection.length, sorters);
        }
        return collection;
    },
    createSortFn: function() {
        var me = this,
            grouper = me.getGrouper(),
            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()
                    });
                    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.un('endupdate', 'onEndUpdateSorters', me);
        }
        if (newSorters) {
            newSorters.on({
                endupdate: 'onEndUpdateSorters',
                scope: me,
                priority: me.$endUpdatePriority
            });
            newSorters.$sortable = me;
        }
        me.onSorterChange();
        me.onEndUpdateSorters(newSorters);
    },
    onSorterChange: function() {
        this._sortFn = null;
    },
    onEndUpdateSorters: function(sorters) {
        var me = this,
            was = me.sorted,
            is = (me.grouped && 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) {
            oldSource.removeObserver(this);
            if (oldSource === auto) {
                auto.destroy();
                this.autoSource = null;
            }
        }
        if (newSource) {
            newSource.addObserver(this);
            if (newSource.length || this.length) {
                this.onCollectionRefresh(newSource);
            }
        }
    }
}, function() {
    var prototype = this.prototype;
    
    prototype.removeAtKey = prototype.removeByKey;
    
    prototype.decodeRemoveItems = prototype.decodeItems;
    Ext.Object.each(prototype._aggregators, function(name) {
        prototype[name] = function(property, begin, end) {
            return this.aggregate(property, name, begin, end);
        };
        prototype[name + 'ByGroup'] = function(property) {
            return this.aggregateByGroup(property, name);
        };
    });
});


Ext.define('Ext.util.ObjectTemplate', {
    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) {
            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);
        }
    },
    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 = [
                {
                    property: me.inverse.field,
                    
                    value: id,
                    exactMatch: true
                }
            ];
        } else if (foreignKeyName) {
            config.filters = [
                {
                    property: foreignKeyName,
                    
                    value: id,
                    exactMatch: true
                }
            ];
            config.foreignKeyName = foreignKeyName;
        }
        if (storeConfig) {
            Ext.apply(config, storeConfig);
        }
        store = Ext.Factory.store(config);
        me.onStoreCreate(store, session, id);
        if (foreignKeyName || (isMany && session)) {
            store.on({
                scope: me,
                add: 'onAddToMany',
                remove: 'onRemoveFromMany',
                clear: 'onRemoveFromMany'
            });
        }
        if (records) {
            store.loadData(records);
            store.complete = !!isComplete;
        }
        return store;
    },
    onStoreCreate: Ext.emptyFn,
    getAssociatedStore: function(inverseRecord, options, scope, records, allowInfer) {
        
        
        
        
        var me = this,
            storeName = me.getStoreName(),
            store = inverseRecord[storeName],
            session = inverseRecord.session,
            load = options && options.reload,
            source = inverseRecord.$source,
            isComplete = false,
            hadSource, args, i, len, raw, rec, sourceStore, hadRecords;
        if (!store) {
            if (session) {
                
                
                
                if (!records && source) {
                    source = source[storeName];
                    if (source && !source.isLoading()) {
                        sourceStore = source;
                        records = [];
                        raw = source.getData().items;
                        for (i = 0 , len = raw.length; i < len; ++i) {
                            rec = raw[i];
                            records.push(session.getRecord(rec.self, rec.id));
                        }
                        isComplete = !!source.complete;
                        hadSource = true;
                    }
                }
                if (!hadSource) {
                    
                    hadRecords = !!records;
                    records = me.findRecords(session, inverseRecord, records, allowInfer);
                    if (!hadRecords && (!records || !records.length)) {
                        records = null;
                    }
                    isComplete = hadRecords;
                }
            } else {
                isComplete = !!(records && records.length > 0);
            }
            store = me.createAssociationStore(session, inverseRecord, records, isComplete);
            store.$source = sourceStore;
            if (!records && (me.autoLoad || options)) {
                load = true;
            }
            inverseRecord[storeName] = store;
        }
        if (options) {
            
            
            if (load || store.isLoading()) {
                store.on('load', function(store, records, success, operation) {
                    args = [
                        store,
                        operation
                    ];
                    scope = scope || options.scope || inverseRecord;
                    if (success) {
                        Ext.callback(options.success, scope, args);
                    } else {
                        Ext.callback(options.failure, scope, args);
                    }
                    args.push(success);
                    Ext.callback(options, scope, args);
                    Ext.callback(options.callback, scope, args);
                }, null, {
                    single: true
                });
            } else {
                
                args = [
                    store,
                    null
                ];
                scope = scope || options.scope || inverseRecord;
                Ext.callback(options.success, scope, args);
                args.push(true);
                Ext.callback(options, scope, args);
                Ext.callback(options.callback, scope, args);
            }
        }
        if (load && !store.isLoading()) {
            store.load();
        }
        return store;
    },
    
    getAssociatedItem: function(rec) {
        var key = this.isMany ? this.getStoreName() : this.getInstanceName();
        return rec[key] || null;
    },
    onDrop: Ext.emptyFn,
    getReaderRoot: function() {
        var me = this;
        return me.associationKey || (me.associationKey = me.association.schema.getNamer().readerRoot(me.role));
    },
    getReader: function() {
        var me = this,
            reader = me.reader,
            Model = me.cls,
            useSimpleAccessors = !me.associationKey,
            root = this.getReaderRoot();
        if (reader && !reader.isReader) {
            if (Ext.isString(reader)) {
                reader = {
                    type: reader
                };
            }
            Ext.applyIf(reader, {
                model: Model,
                rootProperty: root,
                useSimpleAccessors: useSimpleAccessors,
                type: me.defaultReaderType
            });
            reader = me.reader = Ext.createByAlias('reader.' + reader.type, reader);
        }
        return reader;
    },
    getInstanceName: function() {
        var me = this;
        return me.instanceName || (me.instanceName = me.association.schema.getNamer().instanceName(me.role));
    },
    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;
            }
        } 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];
                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;
            }
        },
        createGetter: function() {
            var me = this;
            return function() {
                
                return me.doGet(this);
            };
        },
        createSetter: function() {
            var me = this;
            return function(value) {
                
                return me.doSet(this, value);
            };
        },
        doGet: function(rightRecord) {
            
            
            
            
            
            
            var instanceName = this.getInstanceName(),
                
                ret = rightRecord[instanceName],
                session = rightRecord.session;
            if (!ret && session) {}
            
            
            return ret || null;
        },
        doSet: function(rightRecord, leftRecord) {
            
            
            
            var instanceName = this.getInstanceName(),
                
                ret = rightRecord[instanceName],
                inverseSetter = this.inverse.setterName;
            
            if (ret !== leftRecord) {
                rightRecord[instanceName] = leftRecord;
                if (inverseSetter) {
                    
                    
                    
                    leftRecord[inverseSetter](rightRecord);
                }
            }
            return ret;
        },
        read: function(rightRecord, node, fromReader, readOptions) {
            var me = this,
                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) {
                    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();
                    }
                }
            }
        },
        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 = field.name,
                leftRecord, id, i, len, seen;
            if (!rightRecord.phantom) {
                ret = [];
                if (refs || allowInfer) {
                    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, me, true);
            };
        },
        createSetter: null,
        
        onAddToMany: function(store, leftRecords) {
            this.syncFK(leftRecords, store.getAssociatedEntity(), false);
        },
        onLoadMany: function(rightRecord, leftRecords, session) {
            var instanceName = this.inverse.getInstanceName(),
                id = rightRecord.getId(),
                field = this.association.field,
                i, len, leftRecord, oldId, data, name;
            if (field) {
                for (i = 0 , len = leftRecords.length; i < len; ++i) {
                    leftRecord = leftRecords[i];
                    leftRecord[instanceName] = rightRecord;
                    if (field) {
                        name = field.name;
                        data = leftRecord.data;
                        oldId = data[name];
                        if (oldId !== id) {
                            data[name] = id;
                            if (session) {
                                session.updateReference(leftRecord, field, id, oldId);
                            }
                        }
                    }
                }
            }
        },
        onRemoveFromMany: function(store, leftRecords) {
            this.syncFK(leftRecords, store.getAssociatedEntity(), true);
        },
        read: function(rightRecord, node, fromReader, readOptions) {
            var me = this,
                
                
                instanceName = me.inverse.getInstanceName(),
                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;
                }
            }
        },
        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;
            store = this.getSessionStore(session, leftRecord.get(field.name));
            
            if (store && !store.contains(leftRecord)) {
                store.add(leftRecord);
            }
        },
        onValueChange: function(leftRecord, session, newValue, oldValue) {
            
            
            var me = this,
                instanceName = me.getInstanceName(),
                cls = me.cls,
                hasNewValue, joined, store, i, len, associated, rightRecord;
            if (!leftRecord.changingKey) {
                hasNewValue = newValue || newValue === 0;
                if (!hasNewValue) {
                    leftRecord[instanceName] = null;
                }
                if (session) {
                    
                    store = me.getSessionStore(session, oldValue);
                    if (store) {
                        store.remove(leftRecord);
                    }
                    
                    if (hasNewValue) {
                        store = me.getSessionStore(session, newValue);
                        if (store && !store.isLoading()) {
                            store.add(leftRecord);
                        }
                        if (cls) {
                            rightRecord = session.peekRecord(cls, newValue);
                        }
                        
                        leftRecord[instanceName] = rightRecord || undefined;
                    }
                } else {
                    joined = leftRecord.joined;
                    if (joined) {
                        for (i = 0 , len = joined.length; i < len; ++i) {
                            store = joined[i];
                            if (store.isStore) {
                                associated = store.getAssociatedEntity();
                                if (associated && associated.self === me.cls && associated.getId() === oldValue) {
                                    store.remove(leftRecord);
                                }
                            }
                        }
                    }
                }
            }
            if (me.owner && newValue === null) {
                me.association.schema.queueKeyCheck(leftRecord, me);
            }
        },
        checkKeyForDrop: function(leftRecord) {
            var field = this.association.field;
            if (leftRecord.get(field.name) === null) {
                leftRecord.drop();
            }
        },
        getSessionStore: function(session, value) {
            
            var cls = this.cls,
                rec;
            if (cls) {
                rec = session.peekRecord(cls, value);
                if (rec) {
                    return this.inverse.getAssociatedItem(rec);
                }
            }
        },
        read: function(leftRecord, node, fromReader, readOptions) {
            var 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;
        },
        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: {},
        
        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) {
        var me = this,
            namer = me.getNamer(),
            entities = me.entities,
            associations = me.associations,
            entityName = entityType.entityName,
            association = descr.association,
            legacy = !!descr.legacy,
            child = descr.child,
            parent = descr.parent,
            rightRole = descr.role,
            
            rightType = descr.type || parent || child,
            leftVal = descr.inverse,
            left = Ext.isString(leftVal) ? {
                role: leftVal
            } : leftVal,
            leftRole = left && left.role,
            entry, T;
        if (!rightRole) {
            
            
            
            if (legacy) {
                rightRole = namer.apply('uncapitalize', rightType);
            } else {
                rightRole = namer.apply('fieldRole', referenceField.name);
            }
        }
        if (!leftRole) {
            leftRole = namer.inverseFieldRole(entityName, unique, rightRole, rightType);
        }
        if (!association) {
            if (unique) {
                association = namer.oneToOne(entityType, leftRole, rightType, rightRole);
            } else {
                association = namer.manyToOne(entityType, leftRole, rightType, rightRole);
            }
        }
        
        if (association in associations) {
            Ext.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,
            legacy: descr.legacy,
            left: {
                cls: entityType,
                type: entityName,
                role: leftRole,
                extra: left
            },
            right: {
                cls: entry.cls,
                type: rightType,
                role: rightRole,
                extra: descr
            }
        });
        
        
        
        entityType.associations[rightRole] = association.right;
        entry.associations[leftRole] = association.left;
        if (referenceField) {
            
            
            referenceField.reference = association.right;
            entityType.references.push(referenceField);
        }
        ++me.assocCount;
        me.associationEntityMap[entityName] = true;
        if (entry.cls) {
            me.associationEntityMap[entry.cls.entityName] = true;
        }
        associations[association.name] = association;
        if (association.right.cls) {
            me.decorateModel(association);
        }
    },
    
    privates: {
        
        addEntity: function(entityType) {
            var me = this,
                entities = me.entities,
                entityName = entityType.entityName,
                entry = entities[entityName],
                fields = entityType.fields,
                associations, field, i, length, name;
            if (!entry) {
                entities[entityName] = entry = {
                    name: entityName,
                    associations: {}
                };
            }
            
            else if (entry.cls) {
                Ext.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);
            }
            if (descr.legacy) {
                if (descr.single) {
                    me.addLegacySingle(entityType, descr);
                } else {
                    me.addLegacyHasMany(entityType, descr);
                }
            } else {
                me.addReference(entityType, referenceField, descr, referenceField.unique);
            }
        },
        
        
        
        addPending: function(name, entityType, assoc, type) {
            var pending = this.pending;
            if (!pending[name]) {
                pending[name] = [];
            }
            pending[name].push([
                entityType,
                assoc,
                type
            ]);
        },
        addLegacyBelongsTo: function(entityType, assoc) {
            this.addLegacySingle(entityType, assoc);
        },
        addLegacyHasOne: function(entityType, assoc) {
            this.addLegacySingle(entityType, assoc);
        },
        addLegacySingle: function(entityType, assoc) {
            var foreignKey, name, referenceField;
            assoc = this.constructLegacyAssociation(entityType, assoc);
            assoc.single = true;
            name = assoc.type;
            foreignKey = assoc.foreignKey || (name.toLowerCase() + '_id');
            referenceField = entityType.getField(foreignKey);
            if (referenceField) {
                referenceField.$reference = assoc;
            }
            this.addReference(entityType, referenceField, assoc, true);
        },
        addLegacyHasMany: function(entityType, assoc) {
            var me = this,
                entities = me.entities,
                pending = me.pending,
                associationKey = assoc.associationKey,
                cls, name, referenceField, target, foreignKey, assocName;
            assoc = this.constructLegacyAssociation(entityType, assoc);
            name = assoc.type;
            target = entities[name];
            if (target && target.cls) {
                assoc.type = entityType.entityName;
                foreignKey = assoc.foreignKey || (assoc.type.toLowerCase() + '_id');
                cls = target.cls;
                referenceField = cls.getField(foreignKey);
                assoc.inverse = assoc || {};
                assocName = assoc.name;
                if (assocName || associationKey) {
                    if (assocName) {
                        assoc.inverse.role = assocName;
                    }
                    if (associationKey) {
                        assoc.inverse.associationKey = associationKey;
                    }
                }
                if (referenceField) {
                    referenceField.$reference = assoc;
                }
                
                me.addReference(cls, referenceField, assoc, false);
            } else {
                
                if (!pending[name]) {
                    pending[name] = [];
                }
                pending[name].push([
                    entityType,
                    assoc
                ]);
            }
        },
        constructLegacyAssociation: function(entityType, assoc) {
            if (Ext.isString(assoc)) {
                assoc = {
                    model: assoc
                };
            }
            assoc.legacy = true;
            assoc.type = this.getEntityName(assoc.model);
            var name = assoc.associatedName || assoc.name;
            if (name) {
                assoc.role = name;
            }
            return assoc;
        },
        afterLegacyAssociations: function(cls) {
            var pending = this.pending,
                name = cls.entityName,
                mine = pending[name],
                i, len;
            if (mine) {
                for (i = 0 , len = mine.length; i < len; ++i) {
                    this.addLegacyHasMany.apply(this, mine[i]);
                }
                delete pending[name];
            }
        },
        clear: function(clearNamespace) {
            
            var me = this,
                timer = me.timer;
            delete me.setConfig;
            if (timer) {
                window.clearTimeout(timer);
                me.timer = null;
            }
            me.associations = {};
            me.associationEntityMap = {};
            me.entities = {};
            me.entityClasses = {};
            me.pending = {};
            me.assocCount = me.entityCount = 0;
            if (clearNamespace) {
                me.setNamespace(null);
            }
        },
        constructProxy: function(Model) {
            var me = this,
                data = Ext.Object.chain(Model),
                proxy = me.getProxy();
            data.schema = me;
            data.prefix = me.getUrlPrefix();
            return proxy.apply(data);
        },
        applyDecoration: function(role) {
            var me = this,
                
                
                
                
                cls = role.inverse.cls,
                namer = me.getNamer(),
                getterName, setterName, proto;
            
            
            if (cls && !role.decorated) {
                role.decorated = true;
                proto = cls.prototype;
                if (!(getterName = role.getterName)) {
                    role.getterName = getterName = namer.getterName(role);
                }
                proto[getterName] = role.createGetter();
                
                if (role.createSetter) {
                    if (!(setterName = role.setterName)) {
                        role.setterName = setterName = namer.setterName(role);
                    }
                    proto[setterName] = role.createSetter();
                }
            }
        },
        decorateModel: function(association) {
            this.applyDecoration(association.left);
            this.applyDecoration(association.right);
        },
        processKeyChecks: function(processAll) {
            var me = this,
                keyCheckQueue = me.keyCheckQueue,
                timer = me.timer,
                len, i, item;
            if (timer) {
                window.clearTimeout(timer);
                me.timer = null;
            }
            if (!keyCheckQueue) {
                return;
            }
            
            
            
            
            do {
                keyCheckQueue = me.keyCheckQueue;
                me.keyCheckQueue = [];
                for (i = 0 , len = keyCheckQueue.length; i < len; ++i) {
                    item = keyCheckQueue[i];
                    item.role.checkKeyForDrop(item.record);
                }
            } while (processAll && me.keyCheckQueue.length);
        },
        queueKeyCheck: function(record, role) {
            var me = this,
                keyCheckQueue = me.keyCheckQueue,
                timer = me.timer;
            if (!keyCheckQueue) {
                me.keyCheckQueue = keyCheckQueue = [];
            }
            keyCheckQueue.push({
                record: record,
                role: role
            });
            if (!timer) {
                me.timer = timer = Ext.Function.defer(me.processKeyChecks, me.getKeyCheckDelay(), me);
            }
        },
        rankEntities: function() {
            var me = this,
                entities = me.entities,
                entityNames = Ext.Object.getKeys(entities),
                length = entityNames.length,
                entityType, i;
            me.nextRank = 1;
            
            entityNames.sort();
            for (i = 0; i < length; ++i) {
                entityType = entities[entityNames[i]].cls;
                if (!entityType.rank) {
                    me.rankEntity(entityType);
                }
            }
            
            me.topoStack = null;
        },
        
        
        rankEntity: function(entityType) {
            var associations = entityType.associations,
                associatedType, role, roleName;
            
            var topoStack = this.topoStack || (this.topoStack = []),
                entityName = entityType.entityName;
            topoStack.push(entityName);
            if (entityType.rank === 0) {
                Ext.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
    },
    
    currentPage: 1,
    
    loading: false,
    
    isStore: true,
    
    updating: 0,
    
    constructor: function(config) {
        var me = this,
            storeId;
        
        
        
        
        
        
        
        
        
        
        me.isInitializing = true;
        me.mixins.observable.constructor.call(me, config);
        me.isInitializing = false;
        storeId = me.getStoreId();
        if (!storeId && (config && config.id)) {
            me.setStoreId(storeId = config.id);
        }
        if (storeId) {
            Ext.data.StoreManager.register(me);
        }
    },
    
    getCount: function() {
        return this.getData().getCount();
    },
    
    rangeCached: function(start, end) {
        return this.getData().getCount() >= Math.max(start, end);
    },
    
    
    find: function(property, value, startIndex, anyMatch, caseSensitive, exactMatch) {
        
        
        
        
        
        var startsWith = !anyMatch,
            endsWith = !!(startsWith && exactMatch);
        return this.getData().findIndex(property, value, startIndex, startsWith, endsWith, !caseSensitive);
    },
    
    findRecord: function() {
        var me = this,
            index = me.find.apply(me, arguments);
        return index !== -1 ? me.getAt(index) : null;
    },
    
    findExact: function(property, value, start) {
        return this.getData().findIndexBy(function(rec) {
            return rec.isEqual(rec.get(property), value);
        }, this, start);
    },
    
    findBy: function(fn, scope, start) {
        return this.getData().findIndexBy(fn, scope, start);
    },
    
    getAt: function(index) {
        return this.getData().getAt(index) || null;
    },
    
    getRange: function(start, end, 
    options) {
        
        var result = this.getData().getRange(start, Ext.isNumber(end) ? end + 1 : end);
        
        
        if (options && options.callback) {
            options.callback.call(options.scope || this, result, start, end, options);
        }
        return result;
    },
    
    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, supressEvent) {
        if (Ext.isString(filters)) {
            filters = {
                property: filters,
                value: value
            };
        }
        this.suppressNextFilter = !!supressEvent;
        this.getFilters().add(filters);
        this.suppressNextFilter = false;
    },
    
    removeFilter: function(filter, supressEvent) {
        var me = this,
            filters = me.getFilters();
        me.suppressNextFilter = !!supressEvent;
        if (filter instanceof Ext.util.Filter) {
            filters.remove(filter);
        } else {
            filters.removeByKey(filter);
        }
        me.suppressNextFilter = false;
    },
    updateRemoteSort: function(remoteSort) {
        
        
        this.onRemoteSortSet(this.getSorters(false), remoteSort);
    },
    updateRemoteFilter: function(remoteFilter) {
        this.onRemoteFilterSet(this.getFilters(false), remoteFilter);
    },
    
    addFilter: function(filters, supressEvent) {
        this.suppressNextFilter = !!supressEvent;
        this.getFilters().add(filters);
        this.suppressNextFilter = false;
    },
    
    filterBy: function(fn, scope) {
        this.getFilters().add({
            filterFn: fn,
            scope: scope || this
        });
    },
    
    clearFilter: function(supressEvent) {
        var me = this,
            filters = me.getFilters(false);
        if (!filters || filters.getCount() === 0) {
            return;
        }
        me.suppressNextFilter = !!supressEvent;
        filters.removeAll();
        me.suppressNextFilter = false;
    },
    
    isFiltered: function() {
        return this.getFilters().getCount() > 0;
    },
    
    isSorted: function() {
        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.fireEvent('beginupdate');
        }
    },
    
    endUpdate: function() {
        if (this.updating && !--this.updating) {
            this.fireEvent('endupdate');
            this.onEndUpdate();
        }
    },
    
    getState: function() {
        var me = this,
            sorters = [],
            filters = me.getFilters(),
            grouper = me.getGrouper(),
            filterState, hasState, 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.getStoreId()) {
            Ext.data.StoreManager.unregister(me);
        }
        me.callParent();
        me.onDestroy();
    },
    
    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,
            sorters;
        
        sorters = me.getSorters(false);
        if (me.settingGroups || !sorters) {
            return;
        }
        sorters = sorters.getRange();
        
        if (sorters.length) {
            if (me.getRemoteSort()) {
                me.load({
                    callback: function() {
                        me.fireEvent('sort', me, sorters);
                    }
                });
            } else {
                me.fireEvent('datachanged', me);
                me.fireEvent('refresh', me);
                me.fireEvent('sort', me, sorters);
            }
        } else {
            
            me.fireEvent('sort', me, sorters);
        }
    },
    onFilterEndUpdate: function() {
        var me = this,
            suppressNext = me.suppressNextFilter;
        if (me.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);
        if (grouper && typeof grouper === 'string') {
            grouper = {
                property: grouper,
                direction: direction || me.getGroupDir()
            };
        }
        me.settingGroups = true;
        me.getData().setGrouper(grouper);
        delete me.settingGroups;
        if (change) {
            if (me.getRemoteSort()) {
                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: {
        loadsSynchronously: Ext.privateFn,
        onRemoteFilterSet: function(filters, remoteFilter) {
            if (filters) {
                filters[remoteFilter ? 'on' : 'un']('endupdate', this.onFilterEndUpdate, this);
            }
        },
        
        
        
        
        
        onRemoteSortSet: function(sorters, remoteSort) {
            var me = this;
            if (sorters) {
                sorters[remoteSort ? 'on' : 'un']('endupdate', me.onSorterEndUpdate, me);
                me.getData()[remoteSort ? 'un' : 'on']('beforesort', me.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.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', {
    singleton: true,
    
    none: Ext.identityFn,
    
    stripCommasRe: /,/g,
    
    stripTagsRE: /<\/?[^>]+>/gi,
    
    asText: function(s) {
        
        return (s != null) ? String(s).replace(this.stripTagsRe, '') : '\x00';
    },
    
    asUCText: function(s) {
        
        return (s != null) ? String(s).toUpperCase().replace(this.stripTagsRe, '') : '\x00';
    },
    
    asUCString: function(s) {
        
        return (s != null) ? String(s).toUpperCase() : '\x00';
    },
    
    asDate: function(s) {
        if (!s) {
            return 0;
        }
        if (Ext.isDate(s)) {
            return s.getTime();
        }
        return Date.parse(String(s));
    },
    
    asFloat: function(s) {
        var val = parseFloat(String(s).replace(this.stripCommasRe, ''));
        return isNaN(val) ? 0 : val;
    },
    
    asInt: function(s) {
        var val = parseInt(String(s).replace(this.stripCommasRe, ''), 10);
        return isNaN(val) ? 0 : val;
    }
});


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


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


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


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


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


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


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


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


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


Ext.define('Ext.data.Model', {
    alternateClassName: 'Ext.data.Record',
    
    isEntity: true,
    
    isModel: true,
    
    validIdRe: null,
    erasing: false,
    observableType: 'record',
    constructor: function(data, session) {
        var me = this,
            cls = me.self,
            identifier = cls.identifier,
            Model = Ext.data.Model,
            modelIdentifier = Model.identifier,
            idProperty = me.idField.name,
            array, id, initializeFn, internalId, len, i, fields;
        
        
        
        
        
        me.data = me.data = data || (data = {});
        me.session = session || null;
        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.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;
        }
        if (session) {
            session.add(me);
        }
        if (me.init && Ext.isFunction(me.init)) {
            me.init();
        }
    },
    
    
    editing: false,
    
    dirty: false,
    
    session: null,
    
    dropped: false,
    
    erased: false,
    
    clientIdProperty: null,
    evented: false,
    
    phantom: false,
    
    idProperty: 'id',
    
    manyToMany: null,
    
    identifier: null,
    
    
    
    
    
    
    
    previousValues: undefined,
    
    
    
    proxy: undefined,
    
    
    schema: 'default',
    
    versionProperty: null,
    
    generation: 1,
    
    
    validationSeparator: null,
    
    convertOnSet: true,
    
    
    
    
    
    beginEdit: function() {
        var me = this,
            modified = me.modified,
            previousValues = me.previousValues;
        if (!me.editing) {
            me.editing = true;
            me.editMemento = {
                dirty: me.dirty,
                data: Ext.apply({}, me.data),
                generation: me.generation,
                modified: modified && Ext.apply({}, modified),
                previousValues: previousValues && Ext.apply({}, previousValues)
            };
        }
    },
    
    cancelEdit: function() {
        var me = this,
            editMemento = me.editMemento;
        if (editMemento) {
            me.editing = false;
            
            Ext.apply(me, editMemento);
            me.editMemento = null;
        }
    },
    
    endEdit: function(silent, modifiedFieldNames) {
        var me = this,
            editMemento = me.editMemento;
        if (editMemento) {
            me.editing = false;
            me.editMemento = null;
            
            
            me.previousValues = editMemento.previousValues;
            if (!silent) {
                if (!modifiedFieldNames) {
                    modifiedFieldNames = me.getModifiedFieldNames(editMemento.data);
                }
                if (me.dirty || (modifiedFieldNames && modifiedFieldNames.length)) {
                    me.callJoined('afterEdit', [
                        modifiedFieldNames
                    ]);
                }
            }
        }
    },
    getField: function(name) {
        return this.self.getField(name);
    },
    
    getFields: function() {
        return this.self.getFields();
    },
    getFieldsMap: function() {
        return this.fieldsMap;
    },
    
    getIdProperty: function() {
        return this.idProperty;
    },
    
    getId: function() {
        return this.id;
    },
    
    getObservableId: function() {
        return this.internalId;
    },
    
    setId: function(id, 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,
            currentValue, field, idChanged, key, name, oldId, comparator, dep, dependents, i,
            dirtyRank = 0,
            numFields, newId, rankedFields, reference, value, values;
        if (single) {
            values = me._singleProp;
            values[fieldName] = newValue;
        } else {
            values = fieldName;
        }
        if (!(rankedFields = cls.rankedFields)) {
            
            
            rankedFields = cls.rankFields();
        }
        numFields = rankedFields.length;
        do {
            for (name in values) {
                value = values[name];
                currentValue = data[name];
                comparator = me;
                field = fieldsMap[name];
                if (field) {
                    if (convertOnSet && field.convert) {
                        value = field.convert(value, me);
                    }
                    comparator = field;
                    reference = field.reference;
                } else {
                    reference = null;
                }
                if (comparator.isEqual(currentValue, value)) {
                    
                    continue;
                }
                data[name] = value;
                (modifiedFieldNames || (modifiedFieldNames = [])).push(name);
                (prevVals || (me.previousValues = prevVals = {}))[name] = currentValue;
                if (reference && reference.cls) {
                    if (updateRefs) {
                        session.updateReference(me, field, value, currentValue);
                    }
                    reference.onValueChange(me, session, value, currentValue);
                }
                i = (dependents = field && field.dependents) && dependents.length;
                while (i-- > 0) {
                    (dep = dependents[i]).dirty = true;
                    dirtyRank = dirtyRank ? Math.min(dirtyRank, dep.rank) : dep.rank;
                }
                if (!field || field.persist) {
                    if (modified && modified.hasOwnProperty(name)) {
                        if (!dirty || comparator.isEqual(modified[name], value)) {
                            delete modified[name];
                            me.dirty = -1;
                        }
                    } else if (dirty) {
                        if (!modified) {
                            me.modified = modified = {};
                        }
                        me.dirty = true;
                        modified[name] = currentValue;
                    }
                }
                if (name === me.idField.name) {
                    idChanged = true;
                    oldId = currentValue;
                    newId = value;
                }
            }
            if (!dirtyRank) {
                break;
            }
            field = rankedFields[dirtyRank - 1];
            field.dirty = false;
            if (single) {
                delete values[fieldName];
            } else {
                values = me._singleProp;
                single = true;
            }
            fieldName = field.name;
            values[fieldName] = data[fieldName];
            convertOnSet = true;
            for (; dirtyRank < numFields; ++dirtyRank) {
                if (rankedFields[dirtyRank].dirty) {
                    break;
                }
            }
            if (dirtyRank < numFields) {
                ++dirtyRank;
            } else {
                dirtyRank = 0;
            }
        } while (
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        1);
        if (me.dirty < 0) {
            
            
            me.dirty = false;
            for (key in modified) {
                if (modified.hasOwnProperty(key)) {
                    me.dirty = true;
                    break;
                }
            }
        }
        if (single) {
            
            
            delete values[fieldName];
        }
        ++me.generation;
        if (idChanged) {
            me.id = newId;
            me.callJoined('onIdChanged', [
                oldId,
                newId
            ]);
        }
        if (commit) {
            me.commit(silent, modifiedFieldNames);
        } else if (!silent && !me.editing && modifiedFieldNames) {
            me.callJoined('afterEdit', [
                modifiedFieldNames
            ]);
        }
        return modifiedFieldNames;
    },
    
    reject: function(silent) {
        var me = this,
            modified = me.modified;
        
        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(item) {
        var me = this,
            joined = me.joined;
        
        if (!joined) {
            joined = me.joined = [
                item
            ];
        } else if (!joined.length) {
            joined[0] = item;
        } else {
            
            Ext.Array.include(joined, item);
        }
        if (item.isStore && !me.store) {
            
            me.store = item;
        }
    },
    
    unjoin: function(item) {
        var me = this,
            joined = me.joined,
            
            
            len = joined && joined.length,
            store = me.store,
            i;
        if (len === 1 && joined[0] === item) {
            joined.length = 0;
        } else if (len) {
            Ext.Array.remove(joined, item);
        }
        if (store === item) {
            store = null;
            if (joined) {
                for (i = 0 , len = joined.length; i < len; ++i) {
                    item = joined[i];
                    if (item.isStore) {
                        store = item;
                        break;
                    }
                }
            }
            me.store = store;
        }
    },
    
    clone: function(session) {
        var me = this,
            modified = me.modified,
            ret = me.copy(me.id, session);
        if (modified) {
            
            ret.modified = Ext.apply({}, modified);
        }
        ret.dirty = me.dirty;
        ret.dropped = me.dropped;
        ret.phantom = me.phantom;
        return ret;
    },
    
    copy: function(newId, session) {
        var me = this,
            data = Ext.apply({}, me.data),
            idProperty = me.idProperty,
            T = me.self;
        if (newId || newId === 0) {
            data[idProperty] = newId;
        } else if (newId === null) {
            delete data[idProperty];
        }
        return new T(data, session);
    },
    
    getProxy: function() {
        return this.self.getProxy();
    },
    
    getValidation: function(refresh) {
        var me = this,
            ret = me.validation;
        if (!ret) {
            me.validation = ret = new Ext.data.Validation();
            ret.attach(me);
        }
        if (refresh === true || (refresh !== false && ret.syncGeneration !== me.generation)) {
            ret.refresh(refresh);
        }
        return ret;
    },
    
    validate: function() {
        return new Ext.data.ErrorCollection().init(this);
    },
    
    isValid: function() {
        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.Object.chain(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) {
            me.getAssociatedData(ret, opts);
        }
        
        return ret;
    },
    
    getTransientFields: function() {
        var cls = this.self,
            ret = cls.transientFields;
        if (!ret) {
            cls.rankFields();
            
            ret = cls.transientFields;
        }
        return ret;
    },
    
    isLoading: function() {
        return !!this.loadOperation;
    },
    
    abort: function() {
        var operation = this.loadOperation;
        if (operation) {
            operation.abort();
        }
    },
    
    load: function(options) {
        options = Ext.apply({}, options);
        var me = this,
            scope = options.scope || me,
            proxy = me.getProxy(),
            callback = options.callback,
            operation = me.loadOperation,
            id = me.getId(),
            extras;
        if (operation) {
            
            extras = operation.extraCalls;
            if (!extras) {
                extras = operation.extraCalls = [];
            }
            extras.push(options);
            return operation;
        }
        
        var doIdCheck = true;
        if (me.phantom) {
            doIdCheck = false;
        }
        
        options.id = id;
        
        
        options.recordCreator = function(data, type, readOptions) {
            
            
            
            var session = me.session;
            if (readOptions) {
                readOptions.recordCreator = session ? session.recordCreator : null;
            }
            me.set(data, me._commitOptions);
            
            
            if (doIdCheck && me.getId() !== id) {
                Ext.raise('Invalid record id returned for ' + id + '@' + me.entityName);
            }
            
            return me;
        };
        options.internalCallback = function(operation) {
            var success = operation.wasSuccessful() && operation.getRecords().length > 0,
                op = me.loadOperation,
                extras = op.extraCalls,
                successFailArgs = [
                    me,
                    operation
                ],
                callbackArgs = [
                    me,
                    operation,
                    success
                ],
                i, len;
            me.loadOperation = null;
            if (success) {
                Ext.callback(options.success, scope, successFailArgs);
            } else {
                Ext.callback(options.failure, scope, successFailArgs);
            }
            Ext.callback(callback, scope, callbackArgs);
            
            
            
            if (extras) {
                for (i = 0 , len = extras.length; i < len; ++i) {
                    options = extras[i];
                    if (success) {
                        Ext.callback(options.success, scope, successFailArgs);
                    } else {
                        Ext.callback(options.failure, scope, successFailArgs);
                    }
                    Ext.callback(options.callback, scope, callbackArgs);
                }
            }
            me.callJoined('afterLoad');
        };
        delete options.callback;
        me.loadOperation = operation = proxy.createOperation('read', options);
        operation.execute();
        return operation;
    },
    
    save: function(options) {
        options = Ext.apply({}, options);
        var me = this,
            phantom = me.phantom,
            dropped = me.dropped,
            action = dropped ? 'destroy' : (phantom ? 'create' : 'update'),
            scope = options.scope || me,
            callback = options.callback,
            proxy = me.getProxy(),
            operation;
        options.records = [
            me
        ];
        options.internalCallback = function(operation) {
            var args = [
                    me,
                    operation
                ],
                success = operation.wasSuccessful();
            if (success) {
                Ext.callback(options.success, scope, args);
            } else {
                Ext.callback(options.failure, scope, args);
            }
            args.push(success);
            Ext.callback(callback, scope, args);
        };
        delete options.callback;
        operation = proxy.createOperation(action, options);
        
        
        if (dropped && phantom) {
            
            operation.setResultSet(Ext.data.reader.Reader.prototype.nullResultSet);
            me.setErased();
            operation.setSuccessful(true);
        } else {
            operation.execute();
        }
        return operation;
    },
    
    
    inheritableStatics: {
        
        addFields: function(newFields) {
            this.replaceFields(newFields);
        },
        
        replaceFields: function(newFields, removeFields) {
            var me = this,
                proto = me.prototype,
                Field = Ext.data.field.Field,
                fields = me.fields,
                fieldsMap = me.fieldsMap,
                ordinals = me.fieldOrdinals,
                field, i, idField, len, name, ordinal;
            if (removeFields === true) {
                fields.length = 0;
                me.fieldsMap = fieldsMap = {};
                me.fieldOrdinals = ordinals = {};
            } else if (removeFields) {
                for (i = removeFields.length; i-- > 0; ) {
                    name = removeFields[i];
                    if (name in ordinals) {
                        delete ordinals[name];
                        delete fieldsMap[name];
                    }
                }
                for (i = 0 , len = fields.length; i < len; ++i) {
                    name = (field = fields[i]).name;
                    if (name in ordinals) {
                        ordinals[name] = i;
                    } else {
                        
                        fields.splice(i, 1);
                        --i;
                        --len;
                    }
                }
            }
            
            
            for (i = 0 , len = newFields ? newFields.length : 0; i < len; i++) {
                name = (field = newFields[i]).name;
                if (!(name in ordinals)) {
                    ordinals[name] = ordinal = fields.length;
                    
                    fields.push(field = Field.create(field));
                    fieldsMap[name] = field;
                    field.ordinal = ordinal;
                    field.definedBy = field.owner = this;
                }
            }
            
            
            me.idField = proto.idField = idField = fieldsMap[proto.idProperty];
            idField.allowNull = idField.critical = idField.identifier = true;
            idField.defaultValue = null;
            
            
            me.initializeFn = me.rankedFields = me.transientFields = me.criticalFields = null;
        },
        
        removeFields: function(removeFields) {
            this.replaceFields(null, removeFields);
        },
        
        getIdFromData: function(data) {
            var T = this,
                idField = T.idField,
                id = idField.calculated ? (new T(data)).id : data[idField.name];
            return id;
        },
        
        createWithId: function(id, data, session) {
            var d = data,
                T = this;
            if (id || id === 0) {
                d = {};
                if (data) {
                    Ext.apply(d, data);
                }
                d[T.idField.name] = id;
            }
            return new T(d, session);
        },
        
        getFields: function() {
            return this.fields;
        },
        
        getFieldsMap: function() {
            return this.fieldsMap;
        },
        
        getField: function(name) {
            return this.fieldsMap[name] || null;
        },
        
        getProxy: function() {
            var me = this,
                proxy = me.proxy,
                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 = me.schema.constructProxy(me);
                    proxy = proxy ? Ext.merge(defaults, proxy) : defaults;
                }
                proxy = me.setProxy(proxy);
            }
            return proxy;
        },
        
        setProxy: function(proxy) {
            var me = this,
                model;
            if (proxy) {
                if (!proxy.isProxy) {
                    proxy = Ext.Factory.proxy(proxy);
                } else {
                    model = proxy.getModel();
                    if (model && model !== me) {
                        proxy = proxy.clone();
                    }
                }
                proxy.setModel(me);
            }
            return (me.prototype.proxy = me.proxy = proxy);
        },
        
        load: function(id, options, session) {
            var data = {},
                rec;
            data[this.prototype.idProperty] = id;
            rec = new this(data, session);
            rec.load(options);
            return rec;
        }
    },
    deprecated: {
        5: {
            methods: {
                hasId: null,
                markDirty: null,
                setDirty: null,
                eachStore: function(callback, scope) {
                    var me = this,
                        stores = me.stores,
                        len = stores.length,
                        i;
                    for (i = 0; i < len; ++i) {
                        callback.call(scope, stores[i]);
                    }
                },
                join: function(item) {
                    var me = this,
                        stores = me.stores,
                        joined = me.joined;
                    if (!joined) {
                        joined = me.joined = [
                            item
                        ];
                    } else {
                        joined.push(item);
                    }
                    if (item.isStore) {
                        me.store = me.store || item;
                        if (!stores) {
                            stores = me.stores = [];
                        }
                        stores.push(item);
                    }
                },
                unjoin: function(item) {
                    var me = this,
                        stores = me.stores,
                        joined = me.joined;
                    if (joined.length === 1) {
                        joined.length = 0;
                    } else {
                        Ext.Array.remove(joined, item);
                    }
                    if (item.isStore) {
                        Ext.Array.remove(stores, item);
                        me.store = stores[0] || null;
                    }
                }
            },
            properties: {
                persistenceProperty: null
            },
            inheritableStatics: {
                methods: {
                    setFields: null
                }
            }
        }
    },
    
    privates: {
        _commitOptions: {
            commit: true
        },
        _getChangesOptions: {
            changes: true
        },
        _getAssociatedOptions: {
            associated: true
        },
        _getNotAssociatedOptions: {
            associated: false
        },
        
        copyFrom: function(sourceRecord) {
            var me = this,
                fields = me.fields,
                fieldCount = fields.length,
                modifiedFieldNames = [],
                field,
                i = 0,
                myData, sourceData,
                idProperty = me.idProperty,
                name, value;
            if (sourceRecord) {
                myData = me.data;
                sourceData = sourceRecord.data;
                for (; i < fieldCount; i++) {
                    field = fields[i];
                    name = field.name;
                    
                    
                    
                    
                    
                    
                    if (name !== idProperty) {
                        value = sourceData[name];
                        
                        
                        if (value !== undefined && !me.isEqual(myData[name], value)) {
                            myData[name] = value;
                            modifiedFieldNames.push(name);
                        }
                    }
                }
                
                if (me.phantom && !sourceRecord.phantom) {
                    
                    
                    me.beginEdit();
                    me.setId(sourceRecord.getId());
                    me.endEdit(true);
                    me.commit(true);
                }
            }
            return modifiedFieldNames;
        },
        
        callJoined: function(funcName, args) {
            var me = this,
                joined = me.joined,
                session = me.session,
                i, len, fn, item;
            if (!joined && !session) {
                return;
            }
            if (args) {
                args.unshift(me);
            } else {
                args = [
                    me
                ];
            }
            if (joined) {
                for (i = 0 , len = joined.length; i < len; ++i) {
                    item = joined[i];
                    if (item && (fn = item[funcName])) {
                        fn.apply(item, args);
                    }
                }
            }
            fn = session && session[funcName];
            if (fn) {
                fn.apply(session, args);
            }
        },
        
        setSession: function(session) {
            
            if (session) {
                if (this.session) {
                    Ext.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',
            
            defaultProxy: 'memory',
            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;
                        }
                    }
                }
                
                if (fieldDefs) {
                    delete data.fields;
                    for (i = 0 , length = fieldDefs.length; i < length; ++i) {
                        field = fieldDefs[i];
                        reference = field.reference;
                        
                        
                        if (reference && typeof reference !== 'string') {
                            
                            reference = Ext.merge({}, reference);
                        }
                        field.$reference = reference;
                        field = Field.create(fieldDefs[i]);
                        name = field.name;
                        ordinal = fieldOrdinals[name];
                        if (ordinal === undefined) {
                            
                            fieldOrdinals[name] = ordinal = fields.length;
                        }
                        
                        fieldsMap[name] = field;
                        fields[ordinal] = field;
                        field.definedBy = field.owner = cls;
                        field.ordinal = ordinal;
                        if (name === idProperty) {
                            idDeclared = 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;
                }
            },
            
            
            initValidators: function(data, cls, proto) {
                var superValidators = proto.validators,
                    validators, field, copy, validatorDefs, i, length, fieldValidator, name, validator, item;
                if (superValidators) {
                    validators = {};
                    for (field in superValidators) {
                        validators[field] = Ext.Array.clone(superValidators[field]);
                    }
                }
                validatorDefs = data.validators || data.validations;
                
                if (data.validations) {
                    delete data.validations;
                    Ext.log.warn((cls.$className || 'Ext.data.Model') + ': validations has been deprecated. Please use validators instead.');
                }
                
                if (validatorDefs) {
                    delete data.validators;
                    validators = validators || {};
                    
                    if (Ext.isArray(validatorDefs)) {
                        copy = {};
                        for (i = 0 , length = validatorDefs.length; i < length; ++i) {
                            item = validatorDefs[i];
                            name = item.field;
                            if (!copy[name]) {
                                copy[name] = [];
                            }
                            
                            item = item.fn || item;
                            copy[name].push(item);
                        }
                        validatorDefs = copy;
                    }
                    for (name in validatorDefs) {
                        fieldValidator = validatorDefs[name];
                        if (!Ext.isArray(fieldValidator)) {
                            fieldValidator = [
                                fieldValidator
                            ];
                        }
                        validator = validators[name];
                        if (validators[name]) {
                            
                            Ext.Array.push(validator, fieldValidator);
                        } else {
                            validators[name] = fieldValidator;
                        }
                    }
                }
                if (validators) {
                    for (name in validators) {
                        field = cls.getField(name);
                        if (field) {
                            field.setModelValidators(validators[name]);
                        }
                    }
                }
                cls.validators = proto.validators = validators;
            },
            initAssociations: function(schema, data, cls) {
                
                var associations = data.associations,
                    belongsTo = data.belongsTo,
                    hasMany = data.hasMany,
                    hasOne = data.hasOne,
                    
                    matrices = data.manyToMany,
                    i, length, assoc;
                
                if (data.belongsTo) {
                    Ext.log.warn('Use of "belongsTo" is obsolete' + (cls.$className ? ' in ' + cls.$className : ''));
                    delete data.belongsTo;
                }
                
                delete data.manyToMany;
                if (matrices) {
                    schema.addMatrices(cls, matrices);
                }
                
                delete data.associations;
                delete data.belongsTo;
                delete data.hasMany;
                delete data.hasOne;
                if (associations) {
                    associations = Ext.isArray(associations) ? associations : [
                        associations
                    ];
                    for (i = 0 , length = associations.length; i < length; ++i) {
                        assoc = associations[i];
                        switch (assoc.type) {
                            case 'belongsTo':
                                schema.addLegacyBelongsTo(cls, assoc);
                                break;
                            case 'hasMany':
                                schema.addLegacyHasMany(cls, assoc);
                                break;
                            case 'hasOne':
                                schema.addLegacyHasOne(cls, assoc);
                                break;
                            
                            default:
                                Ext.raise('Invalid association type: "' + assoc.type + '"');
                        }
                    }
                }
                
                if (belongsTo) {
                    belongsTo = Ext.isArray(belongsTo) ? belongsTo : [
                        belongsTo
                    ];
                    for (i = 0 , length = belongsTo.length; i < length; ++i) {
                        schema.addLegacyBelongsTo(cls, belongsTo[i]);
                    }
                }
                if (hasMany) {
                    hasMany = Ext.isArray(hasMany) ? hasMany : [
                        hasMany
                    ];
                    for (i = 0 , length = hasMany.length; i < length; ++i) {
                        schema.addLegacyHasMany(cls, hasMany[i]);
                    }
                }
                if (hasOne) {
                    hasOne = Ext.isArray(hasOne) ? hasOne : [
                        hasOne
                    ];
                    for (i = 0 , length = hasOne.length; i < length; ++i) {
                        schema.addLegacyHasOne(cls, hasOne[i]);
                    }
                }
                schema.afterLegacyAssociations(cls);
            },
            initIdentifier: function(data, cls, proto) {
                var identifier = data.identifier || data.idgen,
                    superIdent = proto.identifier || cls.schema._defaultIdentifier,
                    generatorPrefix;
                
                if (data.idgen) {
                    Ext.log.warn('Ext.data.Model: idgen has been deprecated. Please use identifier instead.');
                }
                
                if (identifier) {
                    delete data.identifier;
                    delete data.idgen;
                    
                    identifier = Ext.Factory.dataIdentifier(identifier);
                } else if (superIdent) {
                    
                    
                    
                    if (superIdent.clone && !superIdent.getId()) {
                        identifier = superIdent.clone();
                    } else if (superIdent.isGenerator) {
                        identifier = superIdent;
                    } else {
                        identifier = Ext.Factory.dataIdentifier(superIdent);
                    }
                }
                cls.identifier = proto.identifier = identifier;
                if (!identifier) {
                    
                    
                    
                    
                    generatorPrefix = cls.entityName;
                    if (!generatorPrefix) {
                        generatorPrefix = Ext.id(null, 'extModel');
                    }
                    cls.identifier = Ext.Factory.dataIdentifier({
                        type: 'sequential',
                        prefix: generatorPrefix + '-'
                    });
                }
            },
            findValidator: function(validators, name, cfg) {
                var type = cfg.type || cfg,
                    field = validators[name],
                    len, i, item;
                if (field) {
                    for (i = 0 , len = field.length; i < len; ++i) {
                        item = field[i];
                        if (item.type === type) {
                            return item;
                        }
                    }
                }
                return null;
            },
            
            makeInitializeFn: function(cls) {
                var code = [
                        'var '
                    ],
                    body = [
                        '\nreturn function (e) {\n    var data = e.data, v;\n'
                    ],
                    fieldVars = [],
                    work = 0,
                    bc, ec, 
                    convert, expr, factory, field, fields, fs, hasDefValue, i, length;
                if (!(fields = cls.rankedFields)) {
                    
                    
                    fields = cls.rankFields();
                }
                for (i = 0 , length = fields.length; i < length; ++i) {
                    
                    
                    
                    field = fields[i];
                    fieldVars[i] = fs = 'f' + i;
                    convert = field.convert;
                    if (i) {
                        code.push(',  \n    ');
                    }
                    code.push(fs, ' = $fields[' + i + ']');
                    
                    
                    code.push('  /*  ', field.name, '  */');
                    
                    
                    
                    
                    
                    if ((hasDefValue = (field.defaultValue !== undefined)) || convert) {
                        
                        
                        
                        
                        expr = 'data["' + field.name + '"]';
                        ++work;
                        bc = ec = '';
                        if (field.cloneDefaultValue) {
                            bc = 'Ext.clone(';
                            ec = ')';
                        }
                        body.push('\n');
                        if (convert && hasDefValue) {
                            
                            
                            
                            
                            
                            
                            
                            
                            
                            
                            
                            body.push('    v = ', expr, ';\n' + '    if (v !== undefined) {\n' + '        v = ', fs, '.convert(v, e);\n' + '    }\n' + '    if (v === undefined) {\n' + '        v = ', bc, fs, '.defaultValue', ec, ';\n' + '    }\n' + '    ', expr, ' = v;');
                        } else if (convert) {
                            
                            
                            
                            
                            
                            
                            body.push('    v = ', fs, '.convert(', expr, ',e);\n' + '    if (v !== undefined) {\n' + '        ', expr, ' = v;\n' + '    }\n');
                        } else if (hasDefValue) {
                            
                            
                            
                            
                            
                            
                            
                            body.push('    if (', expr, ' === undefined) {\n' + '        ', expr, ' = ', bc, fs, '.defaultValue', ec, ';\n' + '    }\n');
                        }
                    }
                }
                if (!work) {
                    
                    return Ext.emptyFn;
                }
                code.push(';\n');
                code.push.apply(code, body);
                code.push('}');
                code = code.join('');
                
                
                factory = new Function('$fields', '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);
        
        
        cls.fields.items = cls.fields;
        if (entityName) {
            schema.addEntity(cls);
            Model.initAssociations(schema, data, cls);
        }
        proxy = data.proxy;
        if (proxy) {
            delete data.proxy;
        } else if (superCls !== Model) {
            proxy = superCls.proxyConfig || superCls.proxy;
        }
        cls.proxyConfig = proxy;
    });
});


Ext.define('Ext.data.ResultSet', {
    
    isResultSet: true,
    $configPrefixed: false,
    config: {
        
        loaded: true,
        
        count: null,
        
        total: null,
        
        success: false,
        
        records: null,
        
        message: null
    },
    
    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: {
        
        totalProperty: 'total',
        
        successProperty: 'success',
        
        rootProperty: '',
        
        messageProperty: '',
        
        typeProperty: '',
        
        implicitIncludes: true,
        
        readRecordsOnFailure: true,
        
        model: null,
        
        proxy: null,
        
        transform: null,
        
        keepRawData: null
    },
    
    
    
    isReader: true,
    
    
    constructor: function(config) {
        if (config && config.hasOwnProperty('root')) {
            config = Ext.apply({}, config);
            config.rootProperty = config.root;
            delete config.root;
            
            Ext.log.error('Ext.data.reader.Reader: Using the deprecated "root" configuration. Use "rootProperty" instead.');
        }
        
        var me = this;
        me.duringInit = 1;
        
        me.mixins.observable.constructor.call(me, config);
        --me.duringInit;
        me.buildExtractors();
    },
    applyModel: function(model) {
        return Ext.data.schema.Schema.lookupEntity(model);
    },
    applyTransform: function(transform) {
        if (transform) {
            if (Ext.isFunction(transform)) {
                transform = {
                    fn: transform
                };
            } else if (transform.charAt) {
                
                transform = {
                    fn: this[transform]
                };
            }
            return transform.fn.bind(transform.scope || this);
        }
        return transform;
    },
    forceBuildExtractors: function() {
        if (!this.duringInit) {
            this.buildExtractors(true);
        }
    },
    updateTotalProperty: function() {
        this.forceBuildExtractors();
    },
    updateMessageProperty: function() {
        this.forceBuildExtractors();
    },
    updateSuccessProperty: function() {
        this.forceBuildExtractors();
    },
    
    read: function(response, readOptions) {
        var data, result;
        if (response) {
            if (response.responseText) {
                result = this.getResponseData(response);
                if (result && result.__$isError) {
                    return new Ext.data.ResultSet({
                        total: 0,
                        count: 0,
                        records: [],
                        success: false,
                        message: result.msg
                    });
                } else {
                    data = this.readRecords(result, readOptions);
                }
            } else {
                data = this.readRecords(response, readOptions);
            }
        }
        return data || this.nullResultSet;
    },
    
    getNullResultSet: function() {
        return this.nullResultSet;
    },
    
    createReadError: function(msg) {
        return {
            __$isError: true,
            msg: msg
        };
    },
    
    readRecords: function(data, readOptions, 
    internalReadOptions) {
        var me = this,
            recordsOnly = internalReadOptions && internalReadOptions.recordsOnly,
            asRoot = internalReadOptions && internalReadOptions.asRoot,
            success, recordCount, records, root, total, value, message, transform;
        transform = this.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)) {
                    total = value;
                }
            }
            if (root) {
                records = me.extractData(root, readOptions);
                recordCount = records.length;
            }
        }
        return recordsOnly ? records : new Ext.data.ResultSet({
            total: total || recordCount,
            count: recordCount,
            records: records,
            success: success,
            message: message
        });
    },
    
    extractData: function(root, readOptions) {
        var me = this,
            entityType = readOptions && readOptions.model ? Ext.data.schema.Schema.lookupEntity(readOptions.model) : me.getModel(),
            schema = entityType.schema,
            includes = schema.hasAssociations(entityType) && me.getImplicitIncludes(),
            fieldExtractorInfo = me.getFieldExtractorInfo(entityType.fieldExtractors),
            length = root.length,
            records = new Array(length),
            typeProperty = me.getTypeProperty(),
            reader, node, nodeType, record, i;
        if (!length && Ext.isObject(root)) {
            root = [
                root
            ];
            length = 1;
        }
        for (i = 0; i < length; i++) {
            record = root[i];
            if (!record.isModel) {
                
                
                node = record;
                
                
                
                
                
                
                if (typeProperty && (nodeType = me.getChildType(schema, node, typeProperty))) {
                    reader = nodeType.getProxy().getReader();
                    record = reader.extractRecord(node, readOptions, nodeType, schema.hasAssociations(nodeType) && reader.getImplicitIncludes(), reader.getFieldExtractorInfo(nodeType.fieldExtractors));
                } else {
                    record = me.extractRecord(node, readOptions, entityType, includes, fieldExtractorInfo);
                }
                
                
                
                
                
                if (record.isModel && record.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.fieldExtractors);
        return this.extractRecord(node, readOptions, entityType, false, fieldExtractorInfo);
    },
    extractRecord: function(node, readOptions, entityType, includes, fieldExtractorInfo) {
        var me = this,
            creatorFn = (readOptions && readOptions.recordCreator) || me.defaultRecordCreator,
            modelData, record;
        
        
        
        modelData = me.extractModelData(node, fieldExtractorInfo);
        record = creatorFn.call(me, modelData, entityType || me.getModel(), readOptions);
        if (includes && record.isModel) {
            me.readAssociated(record, node, readOptions);
        }
        return record;
    },
    getFieldExtractorInfo: function(extractors) {
        
        
        if (!extractors) {
            return;
        }
        var type = this.$className,
            extractor = extractors[type];
        
        
        if (extractor === undefined) {
            extractors[type] = extractor = this.buildFieldExtractors();
        }
        return extractor;
    },
    buildFieldExtractors: function() {
        var fields = this.getFields(),
            len = fields.length,
            buffer = [],
            extractors = [],
            out = null,
            cnt = 0,
            field, name, i, extractor;
        for (i = 0; i < len; ++i) {
            field = fields[i];
            extractor = this.createFieldAccessor(field);
            if (extractor) {
                name = field.name;
                
                buffer.push('val = extractors[' + cnt + '](raw); if (val !== undefined) { data[\'' + name + '\'] = val; }');
                extractors.push(extractor);
                ++cnt;
            }
        }
        if (buffer.length) {
            out = {
                extractors: extractors,
                fn: new Function('raw', 'data', 'extractors', 'var val;' + buffer.join(''))
            };
        }
        return out;
    },
    defaultRecordCreator: function(data, Model) {
        var record = new Model(data);
        
        
        record.phantom = false;
        return record;
    },
    getModelData: function(raw) {
        return {};
    },
    extractModelData: function(raw, fieldExtractorInfo) {
        var data = this.getModelData(raw),
            fn;
        
        if (fieldExtractorInfo) {
            fn = fieldExtractorInfo.fn;
            fn(raw, data, fieldExtractorInfo.extractors);
        }
        return data;
    },
    
    readAssociated: function(record, data, readOptions) {
        var roles = record.associations,
            key, role;
        for (key in roles) {
            if (roles.hasOwnProperty(key)) {
                role = roles[key];
                
                if (role.cls) {
                    role.read(record, data, this, readOptions);
                }
            }
        }
    },
    getFields: function() {
        return this.getModel().fields;
    },
    
    getData: Ext.identityFn,
    
    getRoot: Ext.identityFn,
    
    getResponseData: function(response) {
        
        Ext.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);
            ret = cache.get(key);
            if (!ret) {
                ret = me.createAccessor(prop);
                cache.add(key, ret);
            }
        } else {
            ret = me.createAccessor(prop);
        }
        return ret;
    },
    getAccessorKey: function(prop) {
        return this.$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;
        }
    }
}, 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);
            }
            
            if (reader.onMetaChange) {
                reader.onMetaChange = Ext.Function.createSequence(reader.onMetaChange, me.onMetaChange, me);
            }
        }
    },
    applyWriter: function(writer) {
        var reader = this.getReader();
        writer = Ext.Factory.writer(writer);
        
        
        if (writer.getRecord && !writer.getRecord() && reader && reader.getRecord) {
            reader = reader.getRecord();
            if (reader) {
                writer.setRecord(reader);
            }
        }
        return writer;
    },
    abort: Ext.emptyFn,
    
    onMetaChange: function(meta) {
        this.fireEvent('metachange', this, meta);
    },
    
    create: Ext.emptyFn,
    
    read: Ext.emptyFn,
    
    update: Ext.emptyFn,
    
    erase: Ext.emptyFn,
    
    batch: function(options, 
    listeners) {
        var me = this,
            useBatch = me.getBatchActions(),
            batch, records, actions, aLen, action, a, r, rLen, record;
        if (options.operations === undefined) {
            
            
            options = {
                operations: options,
                listeners: listeners
            };
        }
        if (options.batch) {
            if (Ext.isDefined(options.batch.runOperation)) {
                batch = Ext.applyIf(options.batch, {
                    proxy: me,
                    listeners: {}
                });
            }
        } else {
            options.batch = {
                proxy: me,
                listeners: options.listeners || {}
            };
        }
        if (!batch) {
            batch = new Ext.data.Batch(options.batch);
        }
        batch.on('complete', Ext.bind(me.onBatchComplete, me, [
            options
        ], 0));
        actions = me.getBatchOrder().split(',');
        aLen = actions.length;
        for (a = 0; a < aLen; a++) {
            action = actions[a];
            records = options.operations[action];
            if (records) {
                if (useBatch) {
                    batch.add(me.createOperation(action, {
                        records: records,
                        
                        params: options.params
                    }));
                } else {
                    rLen = records.length;
                    for (r = 0; r < rLen; r++) {
                        record = records[r];
                        batch.add(me.createOperation(action, {
                            records: [
                                record
                            ],
                            
                            params: options.params
                        }));
                    }
                }
            }
        }
        batch.start();
        return batch;
    },
    
    onBatchComplete: function(batchOptions, batch) {
        var scope = batchOptions.scope || this;
        if (batch.hasException()) {
            if (Ext.isFunction(batchOptions.failure)) {
                Ext.callback(batchOptions.failure, scope, [
                    batch,
                    batchOptions
                ]);
            }
        } else if (Ext.isFunction(batchOptions.success)) {
            Ext.callback(batchOptions.success, scope, [
                batch,
                batchOptions
            ]);
        }
        if (Ext.isFunction(batchOptions.callback)) {
            Ext.callback(batchOptions.callback, scope, [
                batch,
                batchOptions
            ]);
        }
    },
    createOperation: function(action, config) {
        var operation = Ext.createByAlias('data.operation.' + action, config);
        operation.setProxy(this);
        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;
    }
});


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


Ext.define('Ext.data.ProxyStore', {
    extend: Ext.data.AbstractStore,
    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,
    
    constructor: function(config) {
        var me = this;
        
        var configModel = me.model;
        
        
        
        
        
        
        
        me.removed = [];
        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(''));
        }
    },
    
    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 
        {
            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();
            }
            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;
        me.fireEvent('datachanged', me);
    },
    
    onBatchException: function(batch, operation) {},
    
    
    
    
    
    
    filterNew: function(item) {
        
        return item.phantom === true && item.isValid();
    },
    
    getNewRecords: function() {
        return [];
    },
    
    getUpdatedRecords: function() {
        return [];
    },
    
    getModifiedRecords: function() {
        return [].concat(this.getNewRecords(), this.getUpdatedRecords());
    },
    
    filterUpdated: function(item) {
        
        return item.dirty === true && item.phantom !== true && item.isValid();
    },
    
    getRemovedRecords: function() {
        var removed = this.getRawRemovedRecords();
        
        return removed ? Ext.Array.clone(removed) : 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;
        
        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;
            operation.execute();
        }
    },
    
    reload: function(options) {
        var o = Ext.apply({}, options, this.lastOptions);
        return this.load(o);
    },
    onEndUpdate: function() {
        var me = this;
        if (me.needsSync && me.autoSync && !me.autoSyncSuspended) {
            me.sync();
        }
    },
    
    afterReject: function(record) {
        var me = this;
        
        
        
        
        
        if (me.contains(record)) {
            me.onUpdate(record, Ext.data.Model.REJECT, null);
            me.fireEvent('update', me, record, Ext.data.Model.REJECT, null);
        }
    },
    
    afterCommit: function(record, modifiedFieldNames) {
        var me = this;
        if (!modifiedFieldNames) {
            modifiedFieldNames = null;
        }
        if (me.contains(record)) {
            me.onUpdate(record, Ext.data.Model.COMMIT, modifiedFieldNames);
            me.fireEvent('update', me, record, Ext.data.Model.COMMIT, modifiedFieldNames);
        }
    },
    afterErase: function(record) {
        this.onErase(record);
    },
    onErase: Ext.emptyFn,
    onUpdate: Ext.emptyFn,
    
    onDestroy: function() {
        var me = this,
            proxy = me.getProxy();
        me.clearLoadTask();
        me.getData().destroy();
        me.data = null;
        me.setProxy(null);
        if (proxy.autoCreated) {
            proxy.destroy();
        }
        me.setModel(null);
    },
    
    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() {
            Ext.asapCancel(this.loadTimer);
            this.pendingLoadOptions = this.loadTimer = null;
        },
        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.data.LocalStore', {
    extend: Ext.Mixin,
    mixinConfig: {
        id: 'localstore'
    },
    config: {
        extraKeys: null
    },
    applyExtraKeys: function(extraKeys) {
        var indexName,
            data = this.getData();
        
        data.setExtraKeys(extraKeys);
        
        extraKeys = data.getExtraKeys();
        for (indexName in extraKeys) {
            this[indexName] = extraKeys[indexName];
        }
    },
    
    add: function(arg) {
        return this.insert(this.getCount(), arguments.length === 1 ? arg : arguments);
    },
    constructDataCollection: function() {
        return new Ext.util.Collection({
            rootProperty: 'data'
        });
    },
    
    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;
    },
    
    
    onCollectionSort: function() {
        this.onSorterEndUpdate();
    },
    
    
    onCollectionFilter: function() {
        this.onFilterEndUpdate();
    },
    notifySorterChange: function() {
        this.getData().onSorterChange();
    },
    forceLocalSort: function() {
        this.getData().onSortChange();
    },
    
    contains: function(record) {
        return this.indexOf(record) > -1;
    },
    
    each: function(fn, scope) {
        var data = this.data.items,
            len = data.length,
            record, i;
        for (i = 0; i < len; ++i) {
            record = data[i];
            if (fn.call(scope || record, record, i, len) === false) {
                break;
            }
        }
    },
    
    collect: function(dataIndex, allowNull, bypassFilter) {
        var me = this,
            data = me.getData();
        if (bypassFilter === true && data.filtered) {
            data = data.getSource();
        }
        return data.collect(dataIndex, 'data', allowNull);
    },
    
    getById: function(id) {
        var data = this.getData();
        if (data.filtered) {
            data = data.getSource();
        }
        return data.get(id) || null;
    },
    
    getByInternalId: function(internalId) {
        var data = this.getData(),
            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: {}
    },
    
    
    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;
        
        
        
        
        
        
        
        me.fireEvent('beginprocessresponse', me, response, operation);
        if (success === true) {
            reader = me.getReader();
            if (response.status === 204) {
                resultSet = reader.getNullResultSet();
            } else {
                resultSet = reader.read(me.extractResponseData(response), {
                    
                    recordCreator: operation.getRecordCreator()
                });
            }
            operation.process(resultSet, request, response);
            exception = !operation.wasSuccessful();
        } else {
            me.setException(operation, response);
            exception = true;
        }
        if (exception) {
            me.fireEvent('exception', me, response, operation);
        }
        me.afterRequest(request, success);
        
        
        
        me.fireEvent('endprocessresponse', me, response, operation);
    },
    
    setException: function(operation, response) {
        operation.setException({
            status: response.status,
            statusText: response.statusText,
            response: response
        });
    },
    
    extractResponseData: Ext.identityFn,
    
    applyEncoding: function(value) {
        return Ext.encode(value);
    },
    
    encodeSorters: function(sorters, preventArray) {
        var out = [],
            length = sorters.length,
            i;
        for (i = 0; i < length; i++) {
            out[i] = sorters[i].serialize();
        }
        return this.applyEncoding(preventArray ? out[0] : out);
    },
    
    encodeFilters: function(filters) {
        var out = [],
            length = filters.length,
            i, op;
        for (i = 0; i < length; i++) {
            out[i] = filters[i].serialize();
        }
        return this.applyEncoding(out);
    },
    
    getParams: function(operation) {
        if (!operation.isReadOperation) {
            return {};
        }
        var me = this,
            params = {},
            grouper = operation.getGrouper(),
            sorters = operation.getSorters(),
            filters = operation.getFilters(),
            page = operation.getPage(),
            start = operation.getStart(),
            limit = operation.getLimit(),
            simpleSortMode = me.getSimpleSortMode(),
            simpleGroupMode = me.getSimpleGroupMode(),
            pageParam = me.getPageParam(),
            startParam = me.getStartParam(),
            limitParam = me.getLimitParam(),
            groupParam = me.getGroupParam(),
            groupDirectionParam = me.getGroupDirectionParam(),
            sortParam = me.getSortParam(),
            filterParam = me.getFilterParam(),
            directionParam = me.getDirectionParam(),
            hasGroups, index;
        if (pageParam && page) {
            params[pageParam] = page;
        }
        if (startParam && (start || start === 0)) {
            params[startParam] = start;
        }
        if (limitParam && limit) {
            params[limitParam] = limit;
        }
        hasGroups = groupParam && grouper;
        if (hasGroups) {
            
            if (simpleGroupMode) {
                params[groupParam] = grouper.getProperty();
                params[groupDirectionParam] = grouper.getDirection();
            } else {
                params[groupParam] = me.encodeSorters([
                    grouper
                ], true);
            }
        }
        if (sortParam && sorters && sorters.length > 0) {
            if (simpleSortMode) {
                index = 0;
                
                if (sorters.length > 1 && hasGroups) {
                    index = 1;
                }
                params[sortParam] = sorters[index].getProperty();
                params[directionParam] = sorters[index].getDirection();
            } else {
                params[sortParam] = me.encodeSorters(sorters);
            }
        }
        if (filterParam && filters && filters.length > 0) {
            params[filterParam] = me.encodeFilters(filters);
        }
        return params;
    },
    
    buildUrl: function(request) {
        var me = this,
            url = me.getUrl(request);
        
        if (!url) {
            Ext.raise("You are using a ServerProxy but have not supplied it with a url.");
        }
        
        if (me.getNoCache()) {
            url = Ext.urlAppend(url, Ext.String.format("{0}={1}", me.getCacheString(), Ext.Date.now()));
        }
        return url;
    },
    
    getUrl: function(request) {
        var url;
        if (request) {
            url = request.getUrl() || this.getApi()[request.getAction()];
        }
        return url ? url : this.callParent();
    },
    
    doRequest: function(operation) {
        
        Ext.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() {
        this.callParent();
        Ext.destroy(this.getReader(), this.getWriter());
        this.reader = this.writer = null;
    }
});


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) {
        var me = this;
        return function(options, success, response) {
            if (request === me.lastRequest) {
                me.lastRequest = null;
            }
            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();
    },
    
    readRecords: function(data, readOptions, 
    internalReadOptions) {
        var me = this,
            meta;
        
        if (me.getMeta) {
            meta = me.getMeta(data);
            if (meta) {
                me.onMetaChange(meta);
            }
        } else if (data.metaData) {
            me.onMetaChange(data.metaData);
        }
        return me.callParent([
            data,
            readOptions,
            internalReadOptions
        ]);
    },
    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() {
        var me = this,
            metaProp, rootProp;
        
        if (me.callParent(arguments)) {
            metaProp = me.getMetaProperty();
            rootProp = me.getRootProperty();
            if (rootProp) {
                me.getRoot = me.getAccessor(rootProp);
            } else {
                me.getRoot = Ext.identityFn;
            }
            if (metaProp) {
                me.getMeta = me.getAccessor(metaProp);
            }
        }
    },
    
    extractData: function(root, readOptions) {
        var recordName = this.getRecord(),
            data = [],
            length, i;
        if (recordName) {
            length = root.length;
            if (!length && Ext.isObject(root)) {
                length = 1;
                root = [
                    root
                ];
            }
            for (i = 0; i < length; i++) {
                data[i] = root[i][recordName];
            }
        } else {
            data = root;
        }
        return this.callParent([
            data,
            readOptions
        ]);
    },
    getModelData: function(raw) {
        return this.getPreserveRawData() ? Ext.apply({}, raw) : raw;
    },
    
    createAccessor: (function() {
        var re = /[\[\.]/;
        return function(expr) {
            var me = this,
                simple = me.getUseSimpleAccessors(),
                operatorIndex, result, current, parts, part, inExpr, isDot, isLeft, isRight, special, c, i, bracketed, len;
            if (!(expr || expr === 0)) {
                return;
            }
            if (typeof expr === 'function') {
                return expr;
            }
            if (!simple) {
                operatorIndex = String(expr).search(re);
            }
            if (simple === true || operatorIndex < 0) {
                result = function(raw) {
                    return raw[expr];
                };
            } else {
                
                
                
                current = 'raw';
                parts = [];
                part = '';
                inExpr = 0;
                len = expr.length;
                
                
                
                for (i = 0; i <= len; ++i) {
                    c = expr[i];
                    isDot = c === '.';
                    isLeft = c === '[';
                    isRight = c === ']';
                    special = isDot || isLeft || isRight || !c;
                    
                    
                    
                    
                    
                    if (!special || inExpr > 1 || (inExpr && !isRight)) {
                        part += c;
                    } else if (special) {
                        bracketed = false;
                        if (isLeft) {
                            ++inExpr;
                        } else if (isRight) {
                            --inExpr;
                            bracketed = true;
                        }
                        if (part) {
                            if (bracketed) {
                                part = '[' + part + ']';
                            } else {
                                part = '.' + part;
                            }
                            current += part;
                            
                            
                            
                            
                            parts.push('' + current);
                            part = '';
                        }
                    }
                }
                result = parts.join(' && ');
                result = Ext.functionFactory('raw', 'return ' + result);
            }
            return result;
        };
    }()),
    
    createFieldAccessor: function(field) {
        
        var me = this,
            mapping = field.mapping,
            hasMap = mapping || mapping === 0,
            map = hasMap ? mapping : field.name;
        if (hasMap) {
            if (typeof map === 'function') {
                return function(raw) {
                    return field.mapping(raw, me);
                };
            } else {
                return me.createAccessor(map);
            }
        }
    },
    getAccessorKey: function(prop) {
        var simple = this.getUseSimpleAccessors() ? 'simple' : '';
        return this.$className + simple + prop;
    },
    privates: {
        copyFrom: function(reader) {
            this.callParent([
                reader
            ]);
            this.getRoot = reader.getRoot;
        }
    }
});


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.Group', {
    extend: Ext.util.Collection,
    config: {
        groupKey: null
    },
    
    
    
    
    $endUpdatePriority: 2001
});


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


Ext.define('Ext.data.Store', {
    extend: Ext.data.ProxyStore,
    alias: 'store.store',
    mixins: [
        Ext.data.LocalStore
    ],
    config: {
        
        data: 0,
        
        
        clearRemovedOnLoad: true,
        
        clearOnPageLoad: true,
        
        associatedEntity: null,
        
        role: null,
        
        session: null
    },
    
    
    addRecordsOptions: {
        addRecords: true
    },
    
    loadCount: 0,
    
    complete: false,
    moveMapCount: 0,
    
    constructor: function(config) {
        var me = this,
            data;
        if (config) {
            if (config.buffered) {
                
                if (this.self !== Ext.data.Store) {
                    Ext.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
        ]);
        me.getData().addObserver(me);
        
        data = me.inlineData;
        if (data) {
            delete me.inlineData;
            me.loadInlineData(data);
        }
    },
    
    
    onCollectionBeginUpdate: function() {
        this.beginUpdate();
    },
    onCollectionEndUpdate: function() {
        this.endUpdate();
    },
    applyData: function(data, dataCollection) {
        
        
        
        var me = this;
        
        me.getFields();
        me.getModel();
        
        if (data && data.isCollection) {
            dataCollection = data;
        } else {
            if (!dataCollection) {
                dataCollection = me.constructDataCollection();
            }
            if (data) {
                if (me.isInitializing) {
                    
                    
                    
                    
                    
                    
                    
                    
                    me.inlineData = data;
                } else {
                    
                    
                    me.loadData(data);
                }
            }
        }
        return dataCollection;
    },
    loadInlineData: function(data) {
        var me = this,
            proxy = me.getProxy();
        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.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;
        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('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;
    },
    
    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);
        }
    },
    fireChangeEvent: function(record) {
        return this.getDataSource().contains(record);
    },
    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 = 0; i < len; ++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() {
        this.callParent(arguments);
        this.callObservers('Filter');
    },
    
    removeAt: function(index, count) {
        var data = this.getData();
        
        index = Math.max(index, 0);
        if (index < data.length) {
            if (arguments.length === 1) {
                count = 1;
            } else if (!count) {
                return;
            }
            data.removeAt(index, count);
        }
    },
    
    removeAll: function(silent) {
        var me = this,
            data = me.getData(),
            hasClear = me.hasListeners.clear,
            records = data.getRange();
        
        if (data.length) {
            
            me.removeIsSilent = true;
            me.callObservers('BeforeRemoveAll');
            data.removeAll();
            me.removeIsSilent = false;
            if (!silent) {
                me.fireEvent('clear', me, records);
                me.fireEvent('datachanged', me);
            }
            me.callObservers('AfterRemoveAll', [
                !!silent
            ]);
        }
        return records;
    },
    
    setRecords: function(records) {
        var count = this.getCount();
        ++this.loadCount;
        if (count) {
            this.getData().splice(0, count, records);
        } else {
            this.add(records);
        }
    },
    
    splice: function(index, toRemove, toAdd) {
        return this.getData().splice(index, toRemove, toAdd);
    },
    
    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);
        } else {
            me.loading = false;
        }
        if (me.hasListeners.load) {
            me.fireEvent('load', me, records, successful, operation);
        }
        me.callObservers('AfterLoad', [
            records,
            successful,
            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);
        }
        ++me.loadCount;
        me.complete = true;
        me.fireEvent('datachanged', me);
        me.fireEvent('refresh', me);
        me.callObservers('AfterPopulate');
    },
    
    
    loadPage: function(page, options) {
        var me = this,
            size = me.getPageSize();
        me.currentPage = page;
        
        options = Ext.apply({
            page: page,
            start: (page - 1) * size,
            limit: size,
            addRecords: !me.getClearOnPageLoad()
        }, options);
        me.read(options);
    },
    
    nextPage: function(options) {
        this.loadPage(this.currentPage + 1, options);
    },
    
    previousPage: function(options) {
        this.loadPage(this.currentPage - 1, options);
    },
    
    clearData: function(isLoad) {
        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);
    },
    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);
    },
    onDestroy: function() {
        var me = this,
            task = me.loadTask,
            data = me.getData(),
            source = data.getSource();
        
        me.clearData();
        me.callParent();
        me.setSession(null);
        me.observers = null;
        if (task) {
            task.cancel();
            me.loadTask = null;
        }
        if (source) {
            source.destroy();
        }
    },
    privates: {
        
        fetch: function(options) {
            options = Ext.apply({}, options);
            this.setLoadOptions(options);
            var operation = this.createOperation('read', options);
            operation.execute();
        },
        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 fields = [
                    'field1'
                ],
                expand = !Ext.isArray(store[0]),
                data = store,
                i, len;
            if (expand) {
                data = [];
                for (i = 0 , len = store.length; i < len; ++i) {
                    data.push([
                        store[i]
                    ]);
                }
            } else {
                for (i = 2 , len = store[0].length; i <= len; ++i) {
                    fields.push('field' + i);
                }
            }
            return new Ext.data.ArrayStore({
                data: data,
                fields: fields,
                autoDestroy: true,
                autoCreated: true,
                expanded: expand
            });
        }
        if (Ext.isString(store)) {
            
            return this.get(store);
        } else {
            
            return Ext.Factory.store(store, defaultType);
        }
    },
    
    getKey: function(o) {
        return o.storeId;
    }
}, function() {
    
    Ext.regStore = function(name, config) {
        var store;
        if (Ext.isObject(name)) {
            config = name;
        } else {
            if (Ext.data.StoreManager.containsKey(name)) {
                return Ext.data.StoreManager.lookup(name);
            }
            config.storeId = name;
        }
        if (config instanceof Ext.data.Store) {
            store = config;
        } else {
            store = new Ext.data.Store(config);
        }
        Ext.data.StoreManager.register(store);
        return store;
    };
    
    Ext.getStore = function(name) {
        return Ext.data.StoreManager.lookup(name);
    };
    
    
    
    var emptyStore = Ext.regStore('ext-empty-store', {
            proxy: 'memory',
            useModelWarning: false
        });
    emptyStore.isEmptyStore = true;
    
    emptyStore.add = emptyStore.remove = emptyStore.insert = emptyStore.loadData = function() {
        Ext.raise('Cannot modify ext-empty-store');
    };
});



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.route.Queue', {
    
    queue: null,
    
    token: null,
    constructor: function(config) {
        Ext.apply(this, config);
        
        this.queue = new Ext.util.MixedCollection();
    },
    
    queueAction: function(route, args) {
        this.queue.add({
            route: route,
            args: args
        });
    },
    
    clearQueue: function() {
        this.queue.removeAll();
    },
    
    runQueue: function() {
        var queue = this.queue,
            action = queue.removeAt(0),
            route;
        if (action) {
            route = action && action.route;
            route.execute(this.token, action.args, this.onActionExecute, this);
        }
    },
    
    onActionExecute: function(clearQueue) {
        if (clearQueue) {
            
            this.clearQueue();
        } else {
            
            this.runQueue();
        }
    }
});


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


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


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


Ext.define('Ext.app.Controller', {
    extend: Ext.app.BaseController,
    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;
            } 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(arguments);
    },
    
    normalizeRefs: function(refs) {
        var me = this,
            newRefs = [];
        if (refs) {
            if (Ext.isObject(refs)) {
                Ext.Object.each(refs, function(key, value) {
                    if (Ext.isString(value)) {
                        value = {
                            selector: value
                        };
                    }
                    value.ref = key;
                    newRefs.push(value);
                });
            } else if (Ext.isArray(refs)) {
                newRefs = Ext.Array.merge(newRefs, refs);
            }
        }
        refs = me.refs;
        if (refs) {
            me.refs = null;
            refs = me.normalizeRefs(refs);
            if (refs) {
                newRefs = Ext.Array.merge(newRefs, refs);
            }
        }
        return newRefs;
    },
    
    getRefMap: function() {
        var me = this,
            refMap = me._refMap,
            refs, ref, ln, i;
        if (!refMap) {
            refs = me.getRefs();
            refMap = me._refMap = {};
            if (refs) {
                for (i = 0 , ln = refs.length; i < ln; i++) {
                    ref = refs[i];
                    refMap[ref.ref] = ref.selector;
                }
            }
        }
        return refMap;
    },
    applyRefs: function(refs) {
        return this.normalizeRefs(Ext.clone(refs));
    },
    
    updateRefs: function(refs) {
        if (refs) {
            this.ref(refs);
        }
    },
    initAutoGetters: function() {
        var proto = this.self.prototype,
            prop, fn;
        for (prop in proto) {
            fn = proto[prop];
            
            
            if (fn && fn['Ext.app.getter']) {
                fn.call(this);
            }
        }
    },
    doInit: function(app) {
        var me = this;
        if (!me._initialized) {
            me.init(app);
            me._initialized = true;
        }
    },
    finishInit: function(app) {
        var me = this,
            controllers = me.controllers,
            controller, i, l;
        if (me._initialized && controllers && controllers.length) {
            for (i = 0 , l = controllers.length; i < l; i++) {
                controller = me.getController(controllers[i]);
                controller.finishInit(app);
            }
        }
    },
    
    init: Ext.emptyFn,
    
    onLaunch: Ext.emptyFn,
    
    activate: function() {
        this.setActive(true);
    },
    
    deactivate: function() {
        this.setActive(false);
    },
    
    isActive: function() {
        return this.getActive();
    },
    ref: function(refs) {
        var me = this,
            i = 0,
            length = refs.length,
            info, ref, fn;
        refs = Ext.Array.from(refs);
        me.references = me.references || [];
        for (; i < length; i++) {
            info = refs[i];
            ref = info.ref;
            fn = 'get' + Ext.String.capitalize(ref);
            if (!me[fn]) {
                me[fn] = Ext.Function.pass(me.getRef, [
                    ref,
                    info
                ], me);
            }
            me.references.push(ref.toLowerCase());
        }
    },
    
    addRef: function(refs) {
        this.ref(refs);
    },
    getRef: function(ref, info, config) {
        var me = this,
            refCache = me.refCache || (me.refCache = {}),
            cached = refCache[ref];
        info = info || {};
        config = config || {};
        Ext.apply(info, config);
        if (info.forceCreate) {
            return Ext.ComponentManager.create(info, 'component');
        }
        if (!cached) {
            if (info.selector) {
                refCache[ref] = cached = Ext.ComponentQuery.query(info.selector)[0];
            }
            if (!cached && info.autoCreate) {
                refCache[ref] = cached = Ext.ComponentManager.create(info, 'component');
            }
            if (cached) {
                cached.on('beforedestroy', function() {
                    refCache[ref] = null;
                });
            }
        }
        return cached;
    },
    
    hasRef: function(ref) {
        var references = this.references;
        return references && Ext.Array.indexOf(references, ref.toLowerCase()) !== -1;
    },
    
    getController: function(id) {
        var app = this.getApplication();
        if (id === this.getId()) {
            return this;
        }
        return app && app.getController(id);
    },
    
    getStore: function(name) {
        var storeId, store;
        storeId = (name.indexOf('@') === -1) ? name : name.split('@')[0];
        store = Ext.StoreManager.get(storeId);
        if (!store) {
            name = Ext.app.Controller.getFullName(name, 'store', this.$namespace);
            if (name) {
                store = Ext.create(name.absoluteName, {
                    
                    
                    id: storeId
                });
            }
        }
        return store;
    },
    
    getModel: function(model) {
        var name = Ext.app.Controller.getFullName(model, 'model', this.$namespace),
            ret = Ext.ClassManager.get(name.absoluteName);
        if (!ret) {
            ret = Ext.data.schema.Schema.lookupEntity(model);
        }
        return ret;
    },
    
    getProfile: function(name) {
        name = Ext.app.Controller.getFullName(name, 'profile', this.$namespace);
        return name;
    },
    
    getView: function(view) {
        var name = Ext.app.Controller.getFullName(view, 'view', this.$namespace);
        return name && Ext.ClassManager.get(name.absoluteName);
    },
    ensureId: function() {
        var id = this.getId();
        if (!id) {
            this.setId(this.getModuleClassName(this.$className, 'controller'));
        }
    },
    destroy: function(destroyRefs, 
    fromApp) {
        var me = this,
            app = me.application,
            refCache, ref;
        if (!fromApp && app) {
            app.unregister(me);
        }
        me.application = null;
        if (destroyRefs) {
            
            refCache = me.refCache;
            for (ref in refCache) {
                if (refCache.hasOwnProperty(ref)) {
                    Ext.destroy(refCache[ref]);
                }
            }
        }
        me.callParent();
    }
});


Ext.define('Ext.app.Application', {
    extend: Ext.app.Controller,
    isApplication: true,
    
    
    
    scope: undefined,
    
    namespaces: [],
    
    paths: null,
    
    
    config: {
        
        name: '',
        
        appProperty: 'app',
        
        
        profiles: [],
        
        currentProfile: null,
        
        
        mainView: {
            $value: null,
            lazy: true
        },
        
        defaultToken: null,
        
        glyphFontFamily: null
    },
    onClassExtended: function(cls, data, hooks) {
        var Controller = Ext.app.Controller,
            proto = cls.prototype,
            requires = [],
            onBeforeClassCreated, paths, namespace, ns;
        
        
        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);
                Ext.require(requires, function() {
                    return onBeforeClassCreated.apply(this, args);
                });
            };
        }
    },
    getDependencies: Ext.emptyFn,
    
    constructor: function(config) {
        var me = this;
        Ext.app.route.Router.application = me;
        me.callParent(arguments);
        
        if (Ext.isEmpty(me.getName())) {
            Ext.raise("[Ext.app.Application] Name property is required");
        }
        
        me.doInit(me);
        me.initNamespace();
        Ext.on('appupdate', me.onAppUpdate, me, {
            single: true
        });
        
        Ext.Loader.setConfig({
            enabled: true
        });
        
        this.onProfilesReady();
    },
    
    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();
    },
    initNamespace: function() {
        var me = this,
            appProperty = me.getAppProperty(),
            ns;
        ns = Ext.namespace(me.getName());
        if (ns) {
            ns.getApplication = function() {
                return me;
            };
            if (appProperty) {
                if (!ns[appProperty]) {
                    ns[appProperty] = me;
                }
                
                else if (ns[appProperty] !== me) {
                    Ext.log.warn('An existing reference is being overwritten for ' + name + '.' + appProperty + '. See the appProperty config.');
                }
            }
        }
    },
    
    initControllers: function() {
        var me = this,
            controllers = Ext.Array.from(me.controllers),
            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.app.route.Router.onStateChange(token);
        } else if (defaultToken) {
            History.add(defaultToken);
        }
        
        
        if (Ext.Microloader && Ext.Microloader.appUpdate && Ext.Microloader.appUpdate.updated) {
            Ext.Microloader.fireAppUpdate();
        }
        
        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();
    },
    
    createController: function(name) {
        return this.getController(name);
    },
    
    destroyController: function(controller) {
        if (typeof controller === 'string') {
            controller = this.getController(controller, true);
        }
        Ext.destroy(controller);
    },
    getController: function(name, 
    preventCreate) {
        var me = this,
            controllers = me.controllers,
            className, controller, len, i, c, all;
        controller = controllers.get(name);
        
        
        
        
        if (!controller) {
            all = controllers.items;
            for (i = 0 , len = all.length; i < len; ++i) {
                c = all[i];
                className = c.getModuleClassName();
                if (className && className === name) {
                    controller = c;
                    break;
                }
            }
        }
        if (!controller && !preventCreate) {
            className = me.getModuleClassName(name, 'controller');
            controller = Ext.create(className, {
                application: me,
                moduleClassName: name
            });
            controllers.add(controller);
            if (me._initialized) {
                controller.doInit(me);
            }
        }
        return controller;
    },
    
    unregister: function(controller) {
        this.controllers.remove(controller);
    },
    getApplication: function() {
        return this;
    },
    destroy: function(destroyRefs) {
        var me = this,
            controllers = me.controllers,
            ns = Ext.namespace(me.getName()),
            appProp = me.getAppProperty();
        Ext.destroy(me.viewport);
        if (controllers) {
            controllers.each(function(controller) {
                controller.destroy(destroyRefs, true);
            });
        }
        me.controllers = null;
        me.callParent([
            destroyRefs,
            true
        ]);
        
        if (ns && ns[appProp] === me) {
            delete ns[appProp];
        }
    },
    updateGlyphFontFamily: function(fontFamily) {
        Ext.setGlyphFontFamily(fontFamily);
    },
    
    applyProfiles: function(profiles) {
        var me = this;
        return Ext.Array.map(profiles, function(profile) {
            return me.getModuleClassName(profile, "profile");
        });
    }
});








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.scroll.Scroller', {
    extend: Ext.Evented,
    alias: 'scroller.scroller',
    mixins: [
        Ext.mixin.Factoryable
    ],
    factoryConfig: {
        defaultType: 'dom'
    },
    isScroller: true,
    _spacerCls: Ext.baseCSSPrefix + 'domscroller-spacer',
    
    
    
    
    config: {
        
        direction: undefined,
        
        
        directionLock: false,
        
        disabled: null,
        
        element: undefined,
        
        indicators: null,
        
        maxPosition: {
            x: 0,
            y: 0
        },
        
        maxUserPosition: {
            x: 0,
            y: 0
        },
        
        minPosition: {
            x: 0,
            y: 0
        },
        
        minUserPosition: {
            x: 0,
            y: 0
        },
        
        momentumEasing: null,
        
        size: null,
        
        slotSnapSize: {
            x: 0,
            y: 0
        },
        
        x: true,
        
        y: true,
        spacerXY: null
    },
    statics: {
        
        create: function(config) {
            return Ext.Factory.scroller(config, Ext.supports.Touch ? 'touch' : 'dom');
        }
    },
    constructor: function(config) {
        var me = this;
        me.position = {
            x: 0,
            y: 0
        };
        me.callParent([
            config
        ]);
        me.onDomScrollEnd = Ext.Function.createBuffered(me.onDomScrollEnd, 100, me);
    },
    destroy: function() {
        var me = this;
        
        me.setX(Ext.emptyString);
        me.setY(Ext.emptyString);
        
        me.setElement(null);
        me.onDomScrollEnd = me._partners = me.component = null;
        me.callParent();
    },
    
    addPartner: function(partner, axis) {
        var me = this,
            partners = me._partners || (me._partners = {}),
            otherPartners = partner._partners || (partner._partners = {});
        partners[partner.getId()] = {
            scroller: partner,
            axis: axis
        };
        otherPartners[me.getId()] = {
            scroller: me,
            axis: axis
        };
    },
    applyElement: function(element, oldElement) {
        var me = this,
            el, eventSource;
        
        if (oldElement) {
            me.scrollListener.destroy();
        }
        if (element) {
            if (element.isElement) {
                el = element;
            } else {
                el = Ext.get(element);
                
                if (!el && (typeof element === 'string')) {
                    Ext.raise("Cannot create Ext.scroll.Scroller instance. " + "Element with id '" + element + "' not found.");
                }
            }
            
            
            
            if (el.dom === document.body) {
                el = Ext.get(document.scrollingElement || (Ext.isWebKit ? document.body : document.documentElement));
                
                
                eventSource = Ext.get(Ext.isIE9m ? window : document);
            } else {
                eventSource = el;
            }
            me.scrollListener = eventSource.on({
                scroll: me.onDomScroll,
                scope: me,
                destroyable: true
            });
            return el;
        }
    },
    
    getClientSize: function() {
        var dom = this.getElement().dom;
        return {
            x: dom.clientWidth,
            y: dom.clientHeight
        };
    },
    
    
    getScrollbarSize: function() {
        var me = this,
            width = 0,
            height = 0,
            element, dom, x, y, hasXScroll, hasYScroll, scrollbarSize;
        if (me.isDomScroller || Ext.supports.touchScroll === 1) {
            element = me.getElement();
            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() {
        
        
        return this.position;
    },
    
    updateDirectionLock: Ext.emptyFn,
    updateDisabled: Ext.emptyFn,
    updateIndicators: Ext.emptyFn,
    updateMaxPosition: Ext.emptyFn,
    updateMaxUserPosition: Ext.emptyFn,
    updateMinPosition: Ext.emptyFn,
    updateMinUserPosition: Ext.emptyFn,
    updateMomenumEasing: Ext.emptyFn,
    updateX: Ext.emptyFn,
    updateY: Ext.emptyFn,
    onPartnerScrollStart: Ext.emptyFn,
    onPartnerScrollEnd: Ext.emptyFn,
    
    
    
    
    
    refresh: function() {
        this.fireEvent('refresh', this);
        return this;
    },
    
    removePartner: function(partner) {
        var partners = this._partners,
            otherPartners = partner._partners;
        if (partners) {
            delete partners[partner.getId()];
        }
        if (otherPartners) {
            delete (otherPartners[this.getId()]);
        }
    },
    
    scrollBy: function(deltaX, deltaY, animate) {
        var position = this.getPosition();
        if (deltaX) {
            if (deltaX.length) {
                
                animate = deltaY;
                deltaY = deltaX[1];
                deltaX = deltaX[0];
            } else if (typeof deltaX !== 'number') {
                
                animate = deltaY;
                deltaY = deltaX.y;
                deltaX = deltaX.x;
            }
        }
        deltaX = (typeof deltaX === 'number') ? deltaX + position.x : null;
        deltaY = (typeof deltaY === 'number') ? deltaY + position.y : null;
        return this.doScrollTo(deltaX, deltaY, animate);
    },
    
    scrollIntoView: function(el, hscroll, animate, highlight) {
        var me = this,
            position = me.getPosition(),
            newPosition, newX, newY,
            myEl = me.getElement();
        
        if (el) {
            newPosition = Ext.fly(el).getScrollIntoViewXY(myEl, position.x, position.y);
            newX = (hscroll === false) ? position.x : newPosition.x;
            newY = newPosition.y;
            if (highlight) {
                me.on({
                    scrollend: 'doHighlight',
                    scope: me,
                    single: true,
                    args: [
                        el,
                        highlight
                    ]
                });
            }
            me.doScrollTo(newX, newY, animate);
        }
    },
    
    isInView: function(el) {
        var me = this,
            result = {
                x: false,
                y: false
            },
            elRegion,
            myEl = me.getElement(),
            myElRegion;
        if (el && myEl.contains(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;
    },
    
    scrollTo: function(x, y, animate) {
        var maxPosition;
        if (x) {
            if (x.length) {
                
                animate = y;
                y = x[1];
                x = x[0];
            } else if (typeof x !== 'number') {
                
                animate = y;
                y = x.y;
                x = x.x;
            }
        }
        if (x < 0 || y < 0) {
            maxPosition = this.getMaxPosition();
            if (x < 0) {
                x += maxPosition.x;
            }
            if (y < 0) {
                y += maxPosition.y;
            }
        }
        this.doScrollTo(x, y, animate);
    },
    updateDirection: function(direction) {
        var me = this,
            x, y;
        if (!direction) {
            
            
            
            x = me.getX();
            y = me.getY();
            if (x && y) {
                direction = (y === 'scroll' && x === 'scroll') ? 'both' : 'auto';
            } else if (y) {
                direction = 'vertical';
            } else if (x) {
                direction = 'horizontal';
            }
            
            
            me._direction = direction;
        } else {
            if (direction === 'auto') {
                x = true;
                y = true;
            } else if (direction === 'vertical') {
                x = false;
                y = true;
            } else if (direction === 'horizontal') {
                x = true;
                y = false;
            } else if (direction === 'both') {
                x = 'scroll';
                y = 'scroll';
            }
            me.setX(x);
            me.setY(y);
        }
    },
    updateSize: function(size) {
        
        
        var me = this,
            element = me.getElement(),
            spacer, x, y;
        if (element) {
            spacer = me.getSpacer();
            
            
            
            
            if (size == null) {
                spacer.hide();
            } else {
                if (typeof size === 'number') {
                    x = size;
                    y = size;
                } else {
                    x = size.x || 0;
                    y = size.y || 0;
                }
                
                if (x > 0) {
                    x -= 1;
                }
                if (y > 0) {
                    y -= 1;
                }
                me.setSpacerXY({
                    x: x,
                    y: y
                });
                spacer.show();
            }
        }
    },
    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: {
        getSpacer: function() {
            var me = this,
                spacer = me._spacer,
                element;
            
            
            
            
            if (!spacer) {
                element = me.getElement();
                spacer = me._spacer = element.createChild({
                    cls: me._spacerCls,
                    role: 'presentation'
                });
                spacer.setVisibilityMode(2);
                
                
                
                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) {
            this.getSpacer().setLocalXY(pos.x, pos.y);
        },
        
        convertX: function(x) {
            return x;
        },
        
        doHighlight: function(el, highlight) {
            if (highlight !== true) {
                
                Ext.fly(el).highlight(highlight);
            } else {
                Ext.fly(el).highlight();
            }
        },
        fireScrollStart: function(x, y) {
            var me = this,
                component = me.component;
            me.invokePartners('onPartnerScrollStart', x, y);
            if (me.hasListeners.scrollstart) {
                me.fireEvent('scrollstart', me, x, y);
            }
            if (component && component.onScrollStart) {
                component.onScrollStart(x, y);
            }
            Ext.GlobalEvents.fireEvent('scrollstart', me, x, y);
        },
        fireScroll: function(x, y) {
            var me = this,
                component = me.component;
            me.invokePartners('onPartnerScroll', x, y);
            if (me.hasListeners.scroll) {
                me.fireEvent('scroll', me, x, y);
            }
            if (component && component.onScrollMove) {
                component.onScrollMove(x, y);
            }
            Ext.GlobalEvents.fireEvent('scroll', me, x, y);
        },
        fireScrollEnd: function(x, y) {
            var me = this,
                component = me.component;
            me.invokePartners('onPartnerScrollEnd', x, y);
            if (me.hasListeners.scrollend) {
                me.fireEvent('scrollend', me, x, y);
            }
            if (component && component.onScrollEnd) {
                component.onScrollEnd(x, y);
            }
            Ext.GlobalEvents.fireEvent('scrollend', me, x, y);
        },
        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) {
            var me = this,
                partners = me._partners,
                partner, id,
                isEnd = method === 'onPartnerScrollEnd';
            
            if (!me.suspendSync & !me.isReflecting) {
                for (id in partners) {
                    partner = partners[id].scroller;
                    partner.isReflecting = true;
                    partner[method](me, x, y);
                    
                    if (isEnd) {
                        partner.isReflecting = false;
                    }
                }
            }
        },
        clearReflecting: function() {
            this.isReflecting = false;
        },
        suspendPartnerSync: function() {
            this.suspendSync = (this.suspendSync || 0) + 1;
        },
        resumePartnerSync: function() {
            if (this.suspendSync) {
                this.suspendSync--;
            }
        },
        updateDomScrollPosition: function() {
            var me = this,
                element = me.getElement(),
                elScroll,
                position = me.position;
            if (element && !element.destroyed) {
                elScroll = me.getElementScroll(element);
                position.x = elScroll.left;
                position.y = elScroll.top;
            }
            me.positionDirty = false;
            return position;
        },
        
        getElementScroll: function(element) {
            return element.getScroll();
        },
        
        
        
        
        
        
        
        
        
        onDomScroll: function() {
            var me = this,
                position, x, y, el;
            
            
            
            
            if (me.isTouchScroller && Ext.supports.touchScroll === 2) {
                el = me.getElement().dom;
                el.scrollTop = el.scrollLeft = 0;
                return;
            }
            position = me.updateDomScrollPosition();
            x = position.x;
            y = position.y;
            if (!me.isScrolling) {
                me.isScrolling = Ext.isScrolling = true;
                me.fireScrollStart(x, y);
            }
            me.fireScroll(x, y);
            
            
            me.onDomScrollEnd();
        },
        onDomScrollEnd: function() {
            var me = this,
                position = me.getPosition(),
                x = position.x,
                y = position.y;
            me.isScrolling = Ext.isScrolling = false;
            me.trackingScrollLeft = x;
            me.trackingScrollTop = y;
            me.fireScrollEnd(x, y);
        },
        onPartnerScroll: function(partner, x, y) {
            var axis = partner._partners[this.getId()].axis;
            if (axis) {
                if (axis === 'x') {
                    y = null;
                } else if (axis === 'y') {
                    x = null;
                }
            }
            this.doScrollTo(x, y, false, true);
        },
        restoreState: function() {
            var me = this,
                el = me.getElement(),
                dom;
            if (el) {
                dom = el.dom;
                
                
                if (me.trackingScrollTop !== undefined) {
                    dom.scrollTop = me.trackingScrollTop;
                    dom.scrollLeft = me.trackingScrollLeft;
                }
            }
        }
    }
});


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


Ext.define('Ext.scroll.DomScroller', {
    extend: Ext.scroll.Scroller,
    alias: 'scroller.dom',
    isDomScroller: true,
    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
        };
    },
    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;
    },
    updateElement: function(element, oldElement) {
        this.initXStyle();
        this.initYStyle();
    },
    updateX: function(x) {
        this.initXStyle();
    },
    updateY: function(y) {
        this.initYStyle();
    },
    privates: {
        doScrollTo: function(x, y, animate) {
            
            
            var me = this,
                element = me.getElement(),
                maxPosition, dom, to, xInf, yInf, i;
            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;
                    }
                }
                x = me.convertX(x);
                if (animate) {
                    to = {};
                    if (y != null) {
                        to.scrollTop = y;
                    }
                    if (x != null) {
                        to.scrollLeft = x;
                    }
                    element.animate(Ext.mergeIf({
                        to: {
                            scrollTop: y,
                            scrollLeft: x
                        }
                    }, animate));
                } else {
                    if (y != null) {
                        dom.scrollTop = y;
                    }
                    if (x != null) {
                        dom.scrollLeft = x;
                        
                        
                        if (Ext.isIE8) {
                            i = dom.scrollLeft;
                            dom.scrollLeft = x;
                        }
                    }
                }
                
                
                me.positionDirty = true;
            }
        },
        
        getElementScroll: function(element) {
            return element.getScroll();
        },
        stopAnimation: function() {
            var anim = this.getElement().getActiveAnimation();
            if (anim) {
                anim.end();
            }
        }
    }
}, function(DomScroller) {
    
    
    
    
    Ext.onDocumentReady(function() {
        DomScroller.document = new DomScroller({
            x: true,
            y: true,
            element: document.body
        });
    });
});

Ext.define('Ext.overrides.scroll.DomScroller', {
    override: 'Ext.scroll.DomScroller',
    _scrollerCls: Ext.baseCSSPrefix + 'domscroller',
    updateElement: function(element, oldElement) {
        element.addCls(this._scrollerCls);
        this.callParent([
            element,
            oldElement
        ]);
    }
});


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


Ext.define('Ext.behavior.Translatable', {
    extend: Ext.behavior.Behavior,
    setConfig: function(config) {
        var translatable = this.translatable,
            component = this.component;
        if (config) {
            if (!translatable) {
                this.translatable = translatable = new Ext.util.Translatable(config);
                translatable.setElement(component.renderElement);
                translatable.on('destroy', 'onTranslatableDestroy', this);
            } else if (Ext.isObject(config)) {
                translatable.setConfig(config);
            }
        } else if (translatable) {
            translatable.destroy();
        }
        return this;
    },
    getTranslatable: function() {
        return this.translatable;
    },
    onTranslatableDestroy: function() {
        delete this.translatable;
    },
    onComponentDestroy: function() {
        var translatable = this.translatable;
        if (translatable) {
            translatable.destroy();
        }
    }
});


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: {}
    },
    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',
            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);
        this.mixins.observable.constructor.call(this, this.initialConfig);
    },
    updateInitialOffset: function(initialOffset) {
        if (typeof initialOffset == 'number') {
            initialOffset = {
                x: initialOffset,
                y: initialOffset
            };
        }
        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, currentInstance);
        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();
        if (!container || !element.dom) {
            return this.defaultConstraint;
        }
        return {
            min: {
                x: 0,
                y: 0
            },
            max: {
                x: this.containerWidth - this.width,
                y: this.containerHeight - this.height
            }
        };
    },
    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
                });
            }
        }
        return container;
    },
    onElementResize: function(element, info) {
        this.width = info.width;
        this.height = info.height;
        this.refresh();
    },
    onContainerResize: function(container, info) {
        this.containerWidth = info.width;
        this.containerHeight = info.height;
        this.refresh();
    },
    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.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.detachListeners();
        if (translatable) {
            translatable.destroy();
        }
        me.callParent();
    }
});


Ext.define('Ext.behavior.Draggable', {
    extend: Ext.behavior.Behavior,
    setConfig: function(config) {
        var draggable = this.draggable,
            component = this.component,
            listeners = this.listeners;
        if (config) {
            if (!draggable) {
                component.setTranslatable(config.translatable);
                this.draggable = draggable = new Ext.util.Draggable(config);
                draggable.setTranslatable(component.getTranslatable());
                draggable.setElement(component.renderElement);
                draggable.on('destroy', 'onDraggableDestroy', this);
                if (listeners) {
                    component.on(listeners);
                }
            } else if (Ext.isObject(config)) {
                draggable.setConfig(config);
            }
        } else if (draggable) {
            draggable.destroy();
        }
        return this;
    },
    getDraggable: function() {
        return this.draggable;
    },
    onDraggableDestroy: function() {
        delete this.draggable;
    },
    onComponentDestroy: function() {
        var draggable = this.draggable;
        if (draggable) {
            draggable.destroy();
        }
    }
});


Ext.define('Ext.Component', {
    extend: Ext.Widget,
    alternateClassName: 'Ext.lib.Component',
    mixins: [
        Ext.mixin.Traversable
    ],
    
    xtype: 'component',
    cachedConfig: {
        
        baseCls: null,
        
        cls: null,
        
        floatingCls: Ext.baseCSSPrefix + 'floating',
        
        hiddenCls: Ext.baseCSSPrefix + 'item-hidden',
        
        ui: null,
        
        margin: null,
        
        padding: null,
        
        border: null,
        
        styleHtmlCls: Ext.baseCSSPrefix + 'html',
        
        styleHtmlContent: null
    },
    eventedConfig: {
        
        left: null,
        
        top: null,
        
        right: null,
        
        bottom: null,
        
        minWidth: null,
        
        minHeight: null,
        
        maxWidth: null,
        
        maxHeight: null,
        
        scrollable: null,
        
        docked: null,
        
        centered: null,
        
        hidden: null,
        
        disabled: null
    },
    config: {
        
        html: null,
        
        draggable: null,
        
        translatable: null,
        
        renderTo: null,
        
        zIndex: null,
        
        tpl: null,
        
        enterAnimation: null,
        
        exitAnimation: null,
        
        showAnimation: null,
        
        hideAnimation: null,
        
        tplWriteMode: 'overwrite',
        
        data: null,
        
        disabledCls: Ext.baseCSSPrefix + 'item-disabled',
        
        contentEl: null,
        
        record: null,
        
        plugins: null,
        
        useBodyElement: null
    },
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    defaultBindProperty: 'html',
    
    alignmentRegex: /^([a-z]+)-([a-z]+)(\?)?$/,
    
    isComponent: true,
    
    floating: false,
    
    rendered: false,
    
    isInner: true,
    
    activeAnimation: null,
    
    dockPositions: {
        top: true,
        right: true,
        bottom: true,
        left: true
    },
    innerElement: null,
    element: {
        reference: 'element',
        classList: [
            'x-unsized'
        ]
    },
    widthLayoutSized: false,
    heightLayoutSized: false,
    layoutStretched: false,
    sizeState: false,
    sizeFlags: 0,
    LAYOUT_WIDTH: 1,
    LAYOUT_HEIGHT: 2,
    LAYOUT_BOTH: 3,
    LAYOUT_STRETCHED: 4,
    _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
        }
    },
    statics: {
        
        fromElement: function(node, limit, selector) {
            return Ext.ComponentManager.fromElement(node, limit, selector);
        }
    },
    initialConfig: null,
    $initParent: null,
    
    constructor: function(config) {
        var me = this,
            plugins = config && config.plugins,
            responsive = 'responsive',
            i, p;
        me.onInitializedListeners = [];
        if (config) {
            me.initialConfig = config;
            
            
            
            me.$initParent = config.$initParent;
        }
        
        
        
        
        if (plugins) {
            plugins = Ext.Array.from(plugins);
            for (i = plugins.length; i-- > 0; ) {
                p = plugins[i];
                if (p === responsive || p.type === responsive) {
                    me.initialConfig = config = Ext.apply({}, config);
                    config.plugins = plugins = plugins.slice(0);
                    
                    
                    plugins[i] = me.createPlugin(p);
                    config = me.initialConfig;
                    break;
                }
            }
        }
        me.callParent([
            config
        ]);
        me.refreshSizeState = me.doRefreshSizeState;
        me.refreshFloating = me.doRefreshFloating;
        if (me.refreshSizeStateOnInitialized) {
            me.refreshSizeState();
        }
        if (me.refreshFloatingOnInitialized) {
            me.refreshFloating();
        }
        me.initialize();
        me.triggerInitialized();
        
        if (me.fullscreen) {
            me.fireEvent('fullscreen', me);
        }
        me.fireEvent('initialize', me);
    },
    beforeInitConfig: function(config) {
        this.beforeInitialize.apply(this, arguments);
    },
    
    beforeInitialize: Ext.emptyFn,
    
    initialize: Ext.emptyFn,
    
    
    
    
    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.innerElement) {
            me.innerElement = me.element;
        }
        if (!me.bodyElement) {
            me.bodyElement = me.innerElement;
        }
    },
    applyPlugins: function(plugins) {
        var me = this,
            config, ln, i, plugin;
        if (!plugins) {
            return plugins;
        }
        plugins = [].concat(plugins);
        for (i = 0 , ln = plugins.length; i < ln; i++) {
            plugins[i] = me.createPlugin(plugins[i]);
        }
        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.setCmp) {
            ret.setCmp(this);
        }
        return ret;
    },
    updatePlugins: function(newPlugins, oldPlugins) {
        var ln, i;
        if (newPlugins) {
            for (i = 0 , ln = newPlugins.length; i < ln; i++) {
                newPlugins[i].init(this);
            }
        }
        if (oldPlugins) {
            for (i = 0 , ln = oldPlugins.length; i < ln; i++) {
                Ext.destroy(oldPlugins[i]);
            }
        }
    },
    applyScrollable: function(scrollable, oldScrollable) {
        var me = this,
            bodyElement, touchScroll, x, y, scrollableCfg;
        if (scrollable) {
            if (scrollable === true || typeof scrollable === 'string') {
                scrollableCfg = me._scrollableCfg[scrollable];
                
                if (!scrollableCfg) {
                    Ext.raise("'" + scrollable + "'is not a valid value for 'scrollable'");
                }
                
                scrollable = scrollableCfg;
            }
            if (oldScrollable) {
                oldScrollable.setConfig(scrollable);
                scrollable = oldScrollable;
            } else {
                touchScroll = Ext.supports.touchScroll;
                if (touchScroll && !scrollable.translatable) {
                    scrollable.translatable = {
                        translationMethod: (touchScroll === 1) ? 'scrollparent' : 'csstransform'
                    };
                }
                if (touchScroll === 1) {
                    
                    
                    scrollable = Ext.Object.chain(scrollable);
                    
                    
                    scrollable.indicators = false;
                }
                scrollable = Ext.scroll.Scroller.create(scrollable);
                scrollable.component = me;
                me.setUseBodyElement(true);
                bodyElement = me.bodyElement;
                if (touchScroll === 2) {
                    scrollable.setInnerElement(me.innerElement);
                    scrollable.setElement(bodyElement);
                } else {
                    if (touchScroll === 1) {
                        
                        
                        
                        
                        
                        x = scrollable.getX();
                        y = scrollable.getY();
                        bodyElement.setStyle({
                            overflowX: x === true ? 'auto' : !x ? 'hidden' : x,
                            overflowY: y === true ? 'auto' : !y ? 'hidden' : y
                        });
                        bodyElement.disableTouchScroll();
                    }
                    scrollable.setElement(bodyElement);
                }
                if (me.isPainted()) {
                    me.onPainted();
                }
                me.on('painted', 'onPainted', me);
            }
        }
        return scrollable;
    },
    onPainted: function() {
        var scrollable = this.getScrollable();
        if (scrollable && scrollable.isTouchScroller && scrollable.getAutoRefresh()) {
            scrollable.refresh();
        }
    },
    updateRenderTo: function(newContainer) {
        this.renderTo(newContainer);
    },
    updateBorder: function(border) {
        this.element.setStyle('border-width', border ? '' : '0');
    },
    updatePadding: function(padding) {
        this.innerElement.setPadding(padding);
    },
    updateMargin: function(margin) {
        this.element.setMargin(margin);
    },
    updateUi: function(newUi, oldUi) {
        var baseCls = this.getBaseCls(),
            element = this.element,
            currentUi = this.currentUi;
        if (baseCls) {
            if (oldUi) {
                if (currentUi) {
                    element.removeCls(currentUi);
                } else {
                    element.removeCls(baseCls + '-' + oldUi);
                }
            }
            if (newUi) {
                element.addCls(newUi, baseCls);
                this.currentUi = baseCls + '-' + newUi;
                
                if (!this.self.prototype.currentUi) {
                    this.self.prototype.currentUi = this.currentUi;
                }
            }
        }
    },
    applyBaseCls: function(baseCls) {
        return baseCls || Ext.baseCSSPrefix + this.xtype;
    },
    updateBaseCls: function(newBaseCls, oldBaseCls) {
        var me = this,
            ui = me.getUi();
        if (oldBaseCls) {
            this.element.removeCls(oldBaseCls);
            if (ui) {
                this.element.removeCls(this.currentUi);
            }
        }
        if (newBaseCls) {
            this.element.addCls(newBaseCls);
            if (ui) {
                this.element.addCls(newBaseCls, null, ui);
                this.currentUi = newBaseCls + '-' + ui;
            }
        }
    },
    
    addCls: function(cls, prefix, suffix) {
        var oldCls = this.getCls(),
            newCls = (oldCls) ? oldCls.slice() : [],
            ln, i, cachedCls;
        prefix = prefix || '';
        suffix = suffix || '';
        if (typeof cls == "string") {
            cls = [
                cls
            ];
        }
        ln = cls.length;
        
        
        
        if (!newCls.length && prefix === '' && suffix === '') {
            newCls = cls;
        } else {
            for (i = 0; i < ln; i++) {
                cachedCls = prefix + cls[i] + suffix;
                if (newCls.indexOf(cachedCls) == -1) {
                    newCls.push(cachedCls);
                }
            }
        }
        this.setCls(newCls);
    },
    
    removeCls: function(cls, prefix, suffix) {
        var oldCls = this.getCls(),
            newCls = (oldCls) ? oldCls.slice() : [],
            ln, i;
        prefix = prefix || '';
        suffix = suffix || '';
        if (typeof cls == "string") {
            newCls = Ext.Array.remove(newCls, prefix + cls + suffix);
        } else {
            ln = cls.length;
            for (i = 0; i < ln; i++) {
                newCls = Ext.Array.remove(newCls, prefix + cls[i] + suffix);
            }
        }
        this.setCls(newCls);
    },
    
    replaceCls: function(oldCls, newCls, prefix, suffix) {
        
        
        var cls = this.getCls(),
            array = (cls) ? cls.slice() : [],
            ln, i, cachedCls;
        prefix = prefix || '';
        suffix = suffix || '';
        
        if (typeof oldCls == "string") {
            array = Ext.Array.remove(array, prefix + oldCls + suffix);
        } else if (oldCls) {
            ln = oldCls.length;
            for (i = 0; i < ln; i++) {
                array = Ext.Array.remove(array, prefix + oldCls[i] + suffix);
            }
        }
        
        if (typeof newCls == "string") {
            array.push(prefix + newCls + suffix);
        } else if (newCls) {
            ln = newCls.length;
            
            
            
            if (!array.length && prefix === '' && suffix === '') {
                array = newCls;
            } else {
                for (i = 0; i < ln; i++) {
                    cachedCls = prefix + newCls[i] + suffix;
                    if (array.indexOf(cachedCls) == -1) {
                        array.push(cachedCls);
                    }
                }
            }
        }
        this.setCls(array);
    },
    
    toggleCls: function(className, 
    state) {
        var oldCls = this.getCls(),
            newCls = oldCls ? oldCls.slice() : [];
        if (typeof state !== 'boolean') {
            state = newCls.indexOf(className) === -1;
        }
        if (state) {
            Ext.Array.include(newCls, className);
        } else {
            Ext.Array.remove(newCls, className);
        }
        this.setCls(newCls);
        return this;
    },
    
    applyCls: function(cls) {
        if (typeof cls == "string") {
            cls = [
                cls
            ];
        }
        
        if (!cls || !cls.length) {
            cls = null;
        }
        return cls;
    },
    
    updateCls: function(newCls, oldCls) {
        var el = this.element;
        if (el && ((newCls && !oldCls) || (!newCls && oldCls) || newCls.length != oldCls.length || Ext.Array.difference(newCls, oldCls).length > 0)) {
            el.replaceCls(oldCls, newCls);
        }
    },
    
    updateStyleHtmlCls: function(newHtmlCls, oldHtmlCls) {
        var innerHtmlElement = this.innerHtmlElement,
            innerElement = this.innerElement;
        if (this.getStyleHtmlContent() && oldHtmlCls) {
            if (innerHtmlElement) {
                innerHtmlElement.replaceCls(oldHtmlCls, newHtmlCls);
            } else {
                innerElement.replaceCls(oldHtmlCls, newHtmlCls);
            }
        }
    },
    applyStyleHtmlContent: function(config) {
        return Boolean(config);
    },
    updateStyleHtmlContent: function(styleHtmlContent) {
        var htmlCls = this.getStyleHtmlCls(),
            innerElement = this.innerElement,
            innerHtmlElement = this.innerHtmlElement;
        if (styleHtmlContent) {
            if (innerHtmlElement) {
                innerHtmlElement.addCls(htmlCls);
            } else {
                innerElement.addCls(htmlCls);
            }
        } else {
            if (innerHtmlElement) {
                innerHtmlElement.removeCls(htmlCls);
            } else {
                innerElement.addCls(htmlCls);
            }
        }
    },
    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();
        }
    },
    updateUseBodyElement: function(useBodyElement) {
        if (useBodyElement) {
            this.link('bodyElement', this.innerElement.wrap({
                cls: 'x-body'
            }));
        }
    },
    
    isCentered: function() {
        return Boolean(this.getCentered());
    },
    isFloating: function() {
        return this.floating;
    },
    isDocked: function() {
        return Boolean(this.getDocked());
    },
    isInnerItem: function() {
        return this.isInner;
    },
    setIsInner: function(isInner) {
        if (isInner !== this.isInner) {
            this.isInner = isInner;
            if (this.initialized) {
                this.fireEvent('innerstatechange', this, isInner);
            }
        }
    },
    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) {
        this.element.setTop(top);
        this.refreshFloating();
    },
    updateRight: function(right) {
        this.element.setRight(right);
        this.refreshFloating();
    },
    updateBottom: function(bottom) {
        this.element.setBottom(bottom);
        this.refreshFloating();
    },
    updateLeft: function(left) {
        this.element.setLeft(left);
        this.refreshFloating();
    },
    updateWidth: function(width) {
        this.element.setWidth(width);
        this.refreshSizeState();
    },
    updateHeight: function(height) {
        this.element.setHeight(height);
        this.refreshSizeState();
    },
    updateFlex: Ext.emptyFn,
    refreshSizeState: function() {
        this.refreshSizeStateOnInitialized = true;
    },
    doRefreshSizeState: function() {
        var hasWidth = this.getWidth() !== null || this.widthLayoutSized || (this.getLeft() !== null && this.getRight() !== null),
            hasHeight = this.getHeight() !== null || this.heightLayoutSized || (this.getTop() !== null && this.getBottom() !== null),
            stretched = this.layoutStretched || this.hasCSSMinHeight || (!hasHeight && this.getMinHeight() !== null),
            state = hasWidth && hasHeight,
            flags = (hasWidth && this.LAYOUT_WIDTH) | (hasHeight && this.LAYOUT_HEIGHT) | (stretched && this.LAYOUT_STRETCHED);
        if (!state && stretched) {
            state = null;
        }
        this.setSizeState(state);
        this.setSizeFlags(flags);
    },
    setLayoutSizeFlags: function(flags) {
        this.layoutStretched = !!(flags & this.LAYOUT_STRETCHED);
        this.widthLayoutSized = !!(flags & this.LAYOUT_WIDTH);
        this.heightLayoutSized = !!(flags & this.LAYOUT_HEIGHT);
        this.refreshSizeState();
    },
    setSizeFlags: function(flags) {
        var me = this,
            el = me.element,
            hasWidth, hasHeight, stretched;
        if (flags !== this.sizeFlags) {
            me.sizeFlags = flags;
            hasWidth = !!(flags & this.LAYOUT_WIDTH);
            hasHeight = !!(flags & this.LAYOUT_HEIGHT);
            stretched = !!(flags & this.LAYOUT_STRETCHED);
            el.toggleCls(Ext.baseCSSPrefix + 'has-width', hasWidth && !stretched && !hasHeight);
            el.toggleCls(Ext.baseCSSPrefix + 'has-height', hasHeight && !stretched && !hasWidth);
            if (me.initialized) {
                me.fireEvent('sizeflagschange', me, flags);
            }
        }
    },
    getSizeFlags: function() {
        if (!this.initialized) {
            this.doRefreshSizeState();
        }
        return this.sizeFlags;
    },
    setSizeState: function(state) {
        if (state !== this.sizeState) {
            this.sizeState = state;
            this.element.setSizeState(state);
            if (this.initialized) {
                this.fireEvent('sizestatechange', this, state);
            }
        }
    },
    getSizeState: function() {
        if (!this.initialized) {
            this.doRefreshSizeState();
        }
        return this.sizeState;
    },
    updateMinWidth: function(width) {
        this.element.setMinWidth(width);
    },
    updateMinHeight: function(height) {
        this.element.setMinHeight(height);
        this.refreshSizeState();
    },
    updateMaxWidth: function(width) {
        this.element.setMaxWidth(width);
    },
    updateMaxHeight: function(height) {
        this.element.setMaxHeight(height);
    },
    
    applyCentered: function(centered) {
        centered = Boolean(centered);
        if (centered) {
            this.refreshInnerState = Ext.emptyFn;
            if (this.isFloating()) {
                this.resetFloating();
            }
            if (this.isDocked()) {
                this.setDocked(false);
            }
            this.setIsInner(false);
            delete this.refreshInnerState;
        }
        return centered;
    },
    updateCentered: function(centered) {
        this.toggleCls(this.getFloatingCls(), centered);
        if (!centered) {
            this.refreshInnerState();
        }
    },
    applyDocked: function(docked) {
        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)", this);
            return;
        }
        
        this.refreshInnerState = Ext.emptyFn;
        if (this.isFloating()) {
            this.resetFloating();
        }
        if (this.isCentered()) {
            this.setCentered(false);
        }
        this.setIsInner(false);
        delete this.refreshInnerState;
        return docked;
    },
    updateDocked: function(docked, oldDocked) {
        this.fireEvent('afterdockedchange', this, docked, oldDocked);
        if (!docked) {
            this.refreshInnerState();
        }
    },
    
    resetFloating: function() {
        this.setTop(null);
        this.setRight(null);
        this.setBottom(null);
        this.setLeft(null);
    },
    refreshInnerState: function() {
        this.setIsInner(!this.isCentered() && !this.isFloating() && !this.isDocked());
    },
    refreshFloating: function() {
        this.refreshFloatingOnInitialized = true;
    },
    doRefreshFloating: function() {
        var me = this,
            floating = true,
            floatingCls = this.getFloatingCls();
        if (me.getTop() === null && me.getBottom() === null && me.getRight() === null && me.getLeft() === null) {
            floating = false;
        } else {
            me.refreshSizeState();
        }
        if (floating !== this.floating) {
            me.floating = floating;
            if (floating) {
                me.refreshInnerState = Ext.emptyFn;
                if (me.isCentered()) {
                    me.setCentered(false);
                }
                if (me.isDocked()) {
                    me.setDocked(false);
                }
                me.setIsInner(false);
                delete me.refreshInnerState;
            }
            me.element.toggleCls(floatingCls, floating);
            if (me.initialized) {
                me.fireEvent('floatingchange', me, floating);
            }
            if (!floating) {
                me.refreshInnerState();
            }
        }
    },
    
    updateFloatingCls: function(newFloatingCls, oldFloatingCls) {
        if (this.isFloating()) {
            this.replaceCls(oldFloatingCls, newFloatingCls);
        }
    },
    applyDisabled: function(disabled) {
        return Boolean(disabled);
    },
    updateDisabled: function(disabled) {
        this.element.toggleCls(this.getDisabledCls(), disabled);
    },
    updateDisabledCls: function(newDisabledCls, oldDisabledCls) {
        if (this.isDisabled()) {
            this.element.replaceCls(oldDisabledCls, newDisabledCls);
        }
    },
    
    disable: function() {
        this.setDisabled(true);
    },
    
    enable: function() {
        this.setDisabled(false);
    },
    
    isDisabled: function() {
        return this.getDisabled();
    },
    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,
            domStyle;
        if (element && !element.destroyed) {
            domStyle = element.dom.style;
            if (zIndex !== null) {
                domStyle.setProperty('z-index', zIndex, 'important');
            } else {
                domStyle.removeProperty('z-index');
            }
        }
    },
    getInnerHtmlElement: function() {
        var innerHtmlElement = this.innerHtmlElement,
            styleHtmlCls;
        if (!innerHtmlElement || !innerHtmlElement.dom || !innerHtmlElement.dom.parentNode) {
            this.innerHtmlElement = innerHtmlElement = Ext.Element.create({
                cls: 'x-innerhtml'
            });
            if (this.getStyleHtmlContent()) {
                styleHtmlCls = this.getStyleHtmlCls();
                this.innerHtmlElement.addCls(styleHtmlCls);
                this.innerElement.removeCls(styleHtmlCls);
            }
            this.innerElement.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);
            }
        }
    },
    applyHidden: function(hidden) {
        return Boolean(hidden);
    },
    updateHidden: function(hidden) {
        var me = this,
            element = me.renderElement;
        if (element.destroyed) {
            return;
        }
        if (hidden) {
            element.hide();
        } else {
            element.show();
        }
        if (me.element) {
            me.element.toggleCls(me.getHiddenCls(), hidden);
        }
        me.fireEvent(hidden ? 'hide' : 'show', me);
    },
    updateHiddenCls: function(newHiddenCls, oldHiddenCls) {
        if (this.isHidden()) {
            this.element.replaceCls(oldHiddenCls, newHiddenCls);
        }
    },
    
    isHidden: function(deep) {
        var hidden = !!this.getHidden(),
            owner;
        if (!hidden && deep) {
            owner = this.getRefOwner();
            while (owner) {
                hidden = !!owner.getHidden();
                if (hidden) {
                    break;
                }
                owner = owner.getRefOwner();
            }
        }
        return hidden;
    },
    
    isVisible: function(deep) {
        return !this.isHidden(deep);
    },
    
    hide: function(animation) {
        var me = this,
            activeAnim = me.activeAnimation;
        me.setCurrentAlignmentInfo(null);
        if (activeAnim) {
            activeAnim.on({
                animationend: function() {
                    me.hide(animation);
                },
                single: true
            });
            return me;
        }
        if (!me.getHidden()) {
            if (animation === undefined || (animation && animation.isComponent)) {
                animation = me.getHideAnimation();
            }
            if (animation) {
                if (animation === true) {
                    animation = 'fadeOut';
                }
                me.on({
                    beforehiddenchange: 'animateFn',
                    scope: this,
                    single: true,
                    args: [
                        animation
                    ]
                });
            }
            me.setHidden(true);
        }
        return me;
    },
    
    show: function(animation) {
        if (this.activeAnimation) {
            this.activeAnimation.on({
                animationend: function() {
                    this.show(animation);
                },
                scope: this,
                single: true
            });
            return this;
        }
        var hidden = this.getHidden();
        if (hidden || hidden === null) {
            if (animation === true) {
                animation = 'fadeIn';
            } else if (animation === undefined || (animation && animation.isComponent)) {
                animation = this.getShowAnimation();
            }
            if (animation) {
                this.beforeShowAnimation();
                this.on({
                    beforehiddenchange: 'animateFn',
                    scope: this,
                    single: true,
                    args: [
                        animation
                    ]
                });
            }
            this.setHidden(false);
        }
        return this;
    },
    beforeShowAnimation: function() {
        var element = this.element;
        if (element) {
            this.renderElement.show();
            element.removeCls(this.getHiddenCls());
        }
    },
    animateFn: function(animation, component, newState, oldState, controller) {
        var me = this;
        if (animation && (!newState || (newState && me.isPainted()))) {
            me.activeAnimation = new Ext.fx.Animation(animation);
            me.activeAnimation.setElement(component.element);
            if (!Ext.isEmpty(newState)) {
                me.activeAnimation.setOnEnd(function() {
                    me.activeAnimation = null;
                    controller.resume();
                });
                controller.pause();
            }
            Ext.Animator.run(me.activeAnimation);
        }
    },
    
    setVisibility: function(isVisible) {
        this.renderElement.setVisible(isVisible);
    },
    
    isRendered: function() {
        return this.rendered;
    },
    
    isPainted: function() {
        return this.renderElement.isPainted();
    },
    
    applyTpl: function(config) {
        return (Ext.isObject(config) && config.isTemplate) ? config : new Ext.XTemplate(config);
    },
    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) {
            var tpl = me.getTpl(),
                tplWriteMode = me.getTplWriteMode();
            if (tpl) {
                tpl[tplWriteMode](me.getInnerHtmlElement(), newData);
            }
            
            this.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.updateData(newRecord.getData(true));
        }
    },
    
    afterEdit: function() {
        this.updateRecord(this.getRecord());
    },
    
    afterErase: function() {
        this.setRecord(null);
    },
    
    getXTypes: function() {
        return this.xtypesChain.join('/');
    },
    getDraggableBehavior: function() {
        var behavior = this.draggableBehavior;
        if (!behavior) {
            behavior = this.draggableBehavior = new Ext.behavior.Draggable(this);
        }
        return behavior;
    },
    applyDraggable: function(config) {
        this.getDraggableBehavior().setConfig(config);
    },
    getDraggable: function() {
        return this.getDraggableBehavior().getDraggable();
    },
    getTranslatableBehavior: function() {
        var behavior = this.translatableBehavior;
        if (!behavior) {
            behavior = this.translatableBehavior = new Ext.behavior.Translatable(this);
        }
        return behavior;
    },
    applyTranslatable: function(config) {
        this.getTranslatableBehavior().setConfig(config);
    },
    getTranslatable: function() {
        return this.getTranslatableBehavior().getTranslatable();
    },
    translateAxis: function(axis, value, animation) {
        var x, y;
        if (axis === 'x') {
            x = value;
        } else {
            y = value;
        }
        return this.translate(x, y, animation);
    },
    translate: function() {
        var translatable = this.getTranslatable();
        if (!translatable) {
            this.setTranslatable(true);
            translatable = this.getTranslatable();
        }
        translatable.translate.apply(translatable, arguments);
    },
    
    showBy: function(component, alignment) {
        var me = this,
            viewport = Ext.Viewport,
            parent = me.getParent();
        me.setVisibility(false);
        if (parent !== viewport) {
            viewport.add(me);
        }
        me.show();
        me.on({
            hide: 'onShowByErased',
            destroy: 'onShowByErased',
            single: true,
            scope: me
        });
        viewport.on('resize', 'alignTo', me, {
            args: [
                component,
                alignment
            ]
        });
        me.alignTo(component, alignment);
        me.setVisibility(true);
    },
    
    onShowByErased: function() {
        Ext.Viewport.un('resize', 'alignTo', this);
    },
    
    getAlignmentInfo: function(component, alignment) {
        var alignToElement = component.isComponent ? component.renderElement : component,
            alignToBox = alignToElement.getBox(),
            element = this.renderElement,
            box = element.getBox(),
            stats = {
                alignToBox: alignToBox,
                alignment: alignment,
                top: alignToBox.top,
                left: alignToBox.left,
                alignToWidth: alignToBox.width,
                alignToHeight: alignToBox.height,
                width: box.width,
                height: box.height
            },
            currentAlignmentInfo = this.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) {
        var alignmentInfo = this.getAlignmentInfo(component, alignment);
        if (alignmentInfo.isAligned)  {
            return;
        }
        
        var alignToBox = alignmentInfo.stats.alignToBox,
            constrainBox = this.getParent().element.getBox(),
            alignToHeight = alignmentInfo.stats.alignToHeight,
            alignToWidth = alignmentInfo.stats.alignToWidth,
            height = alignmentInfo.stats.height,
            width = alignmentInfo.stats.width;
        
        constrainBox.bottom -= 5;
        constrainBox.height -= 10;
        constrainBox.left += 5;
        constrainBox.right -= 5;
        constrainBox.top += 5;
        constrainBox.width -= 10;
        if (!alignment || alignment === 'auto') {
            if (constrainBox.bottom - alignToBox.bottom < height) {
                if (alignToBox.top - constrainBox.top < height) {
                    if (alignToBox.left - constrainBox.left < width) {
                        alignment = 'cl-cr?';
                    } else {
                        alignment = 'cr-cl?';
                    }
                } else {
                    alignment = 'bc-tc?';
                }
            } else {
                alignment = 'tc-bc?';
            }
        }
        var matches = alignment.match(this.alignmentRegex);
        
        if (!matches) {
            Ext.Logger.error("Invalid alignment value of '" + alignment + "'");
        }
        
        var from = matches[1].split(''),
            to = matches[2].split(''),
            constrained = (matches[3] === '?'),
            fromVertical = from[0],
            fromHorizontal = from[1] || fromVertical,
            toVertical = to[0],
            toHorizontal = to[1] || toVertical,
            top = alignToBox.top,
            left = alignToBox.left,
            halfAlignHeight = alignToHeight / 2,
            halfAlignWidth = alignToWidth / 2,
            halfWidth = width / 2,
            halfHeight = height / 2,
            maxLeft, maxTop;
        switch (fromVertical) {
            case 't':
                switch (toVertical) {
                    case 'c':
                        top += halfAlignHeight;
                        break;
                    case 'b':
                        top += alignToHeight;
                };
                break;
            case 'b':
                switch (toVertical) {
                    case 'c':
                        top -= (height - halfAlignHeight);
                        break;
                    case 't':
                        top -= height;
                        break;
                    case 'b':
                        top -= height - alignToHeight;
                };
                break;
            case 'c':
                switch (toVertical) {
                    case 't':
                        top -= halfHeight;
                        break;
                    case 'c':
                        top -= (halfHeight - halfAlignHeight);
                        break;
                    case 'b':
                        top -= (halfHeight - alignToHeight);
                };
                break;
        }
        switch (fromHorizontal) {
            case 'l':
                switch (toHorizontal) {
                    case 'c':
                        left += halfAlignHeight;
                        break;
                    case 'r':
                        left += alignToWidth;
                };
                break;
            case 'r':
                switch (toHorizontal) {
                    case 'r':
                        left -= (width - alignToWidth);
                        break;
                    case 'c':
                        left -= (width - halfWidth);
                        break;
                    case 'l':
                        left -= width;
                };
                break;
            case 'c':
                switch (toHorizontal) {
                    case 'l':
                        left -= halfWidth;
                        break;
                    case 'c':
                        left -= (halfWidth - halfAlignWidth);
                        break;
                    case 'r':
                        left -= (halfWidth - alignToWidth);
                };
                break;
        }
        if (constrained) {
            maxLeft = (constrainBox.left + constrainBox.width) - width;
            maxTop = (constrainBox.top + constrainBox.height) - height;
            left = Math.max(constrainBox.left, Math.min(maxLeft, left));
            top = Math.max(constrainBox.top, Math.min(maxTop, top));
        }
        this.setLeft(left);
        this.setTop(top);
        this.setCurrentAlignmentInfo(alignmentInfo);
    },
    
    up: function(selector) {
        var result = this.parent;
        if (selector) {
            for (; result; result = result.parent) {
                if (Ext.ComponentQuery.is(result, selector)) {
                    return result;
                }
            }
        }
        return result;
    },
    getBubbleTarget: function() {
        return this.getParent();
    },
    
    destroy: function() {
        var me = this;
        
        me.isDestroying = me.destroying = true;
        if (me.hasListeners.destroy) {
            me.fireEvent('destroy', me);
        }
        Ext.destroy(me.getTranslatable(), me.getPlugins(), me.innerHtmlElement, me.scrollerElement, me.getScrollable());
        me.setRecord(null);
        me.callParent();
        
        me.isDestroying = me.destroying = false;
    },
    privates: {
        doAddListener: function(name, fn, scope, options, order, caller, manager) {
            if (name == 'painted' || name == 'resize') {
                this.element.doAddListener(name, fn, scope || this, options, order);
            }
            this.callParent([
                name,
                fn,
                scope,
                options,
                order,
                caller,
                manager
            ]);
        },
        doRemoveListener: function(name, fn, scope) {
            if (name == 'painted' || name == 'resize') {
                this.element.doRemoveListener(name, fn, scope);
            }
            this.callParent([
                name,
                fn,
                scope
            ]);
        }
    }
}, function() {
    
    var metaTags = document.getElementsByTagName('head')[0].getElementsByTagName('meta'),
        len = metaTags.length,
        i, hasViewport;
    for (i = 0; i < len; i++) {
        if (metaTags[i].name === 'viewport') {
            hasViewport = true;
        }
    }
    if (!hasViewport) {
        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=1, user-scalable=no">');
    }
});



Ext.define('Ext.layout.Abstract', {
    mixins: [
        Ext.mixin.Observable
    ],
    isLayout: true,
    constructor: function(config) {
        this.initialConfig = config;
    },
    
    isCompatible: function(layout) {
        if (!layout) {
            return true;
        }
        if (layout.isInstance) {
            return false;
        }
        var type = Ext.isString(layout) ? layout : layout.type,
            alias = this.alias;
        if (!alias || !type) {
            return false;
        }
        return alias.indexOf('layout.' + type) > -1;
    },
    
    setContainer: function(container) {
        var me = this;
        me.container = container;
        me.mixins.observable.constructor.call(me, me.initialConfig);
        return me;
    },
    onItemAdd: Ext.emptyFn,
    onItemRemove: Ext.emptyFn,
    onItemMove: Ext.emptyFn,
    onItemCenteredChange: Ext.emptyFn,
    onItemFloatingChange: Ext.emptyFn,
    onItemDockedChange: Ext.emptyFn,
    onItemInnerStateChange: Ext.emptyFn
});


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


Ext.define('Ext.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'
        },
        bodyElement: {
            className: Ext.baseCSSPrefix + 'dock-body'
        },
        innerWrapper: null,
        sizeState: false,
        container: null
    },
    positionMap: {
        top: 'start',
        left: 'start',
        bottom: 'end',
        right: 'end'
    },
    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.getBodyElement();
        }
        this.itemsCount++;
        if (position === 'start') {
            element.insertBefore(referenceElement);
        } else {
            element.insertAfter(referenceElement);
        }
    },
    removeItem: function(item) {
        var position = item.getDocked(),
            items = this.items[this.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 (--this.itemsCount === 0) {
            this.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());
    },
    applyBodyElement: function(bodyElement) {
        return Ext.Element.create(bodyElement);
    },
    updateBodyElement: function(bodyElement) {
        this.getElement().append(bodyElement);
    },
    updateInnerWrapper: function(innerWrapper, oldInnerWrapper) {
        var bodyElement = this.getBodyElement();
        if (oldInnerWrapper && oldInnerWrapper.$outerWrapper === this) {
            oldInnerWrapper.getElement().detach();
            delete oldInnerWrapper.$outerWrapper;
        }
        if (innerWrapper) {
            innerWrapper.setSizeState(this.getSizeState());
            innerWrapper.$outerWrapper = this;
            bodyElement.append(innerWrapper.getElement());
        }
    },
    updateSizeState: function(state) {
        var innerWrapper = this.getInnerWrapper();
        this.getElement().setSizeState(state);
        if (innerWrapper) {
            innerWrapper.setSizeState(state);
        }
    },
    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.setInnerWrapper(null);
        me.unlink([
            '_bodyElement',
            '_element'
        ]);
        me.callParent();
    }
});


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


Ext.define('Ext.layout.Default', {
    extend: Ext.layout.Abstract,
    isAuto: true,
    alias: [
        'layout.default',
        'layout.auto'
    ],
    config: {
        
        animation: null
    },
    centerWrapperClass: 'x-center',
    dockWrapperClass: 'x-dock',
    positionMap: {
        top: 'start',
        left: 'start',
        middle: 'center',
        bottom: 'end',
        right: 'end'
    },
    positionDirectionMap: {
        top: 'vertical',
        bottom: 'vertical',
        left: 'horizontal',
        right: 'horizontal'
    },
    setContainer: function(container) {
        var me = this;
        me.dockedItems = [];
        me.callParent([
            container
        ]);
        if (container.initialized) {
            me.onContainerInitialized();
        } else {
            container.onInitialized('onContainerInitialized', me);
        }
    },
    onContainerInitialized: function() {
        var me = this;
        me.handleDockedItemBorders();
        me.container.on({
            delegate: '> component',
            beforecenteredchange: 'onItemCenteredChange',
            beforefloatingchange: 'onItemFloatingChange',
            afterdockedchange: 'onAfterItemDockedChange',
            
            scope: me
        });
    },
    monitorSizeStateChange: function() {
        this.monitorSizeStateChange = Ext.emptyFn;
        this.container.on('sizestatechange', 'onContainerSizeStateChange', this);
    },
    monitorSizeFlagsChange: function() {
        this.monitorSizeFlagsChange = Ext.emptyFn;
        this.container.on('sizeflagschange', 'onContainerSizeFlagsChange', this);
    },
    onItemAdd: function(item) {
        var docked = item.getDocked();
        if (docked != null) {
            this.dockItem(item);
        } else if (item.isCentered()) {
            this.onItemCenteredChange(item, true);
        } else if (item.isFloating()) {
            this.onItemFloatingChange(item, true);
        } else {
            this.onItemInnerStateChange(item, true);
        }
    },
    
    onItemInnerStateChange: function(item, isInner, destroying) {
        if (isInner) {
            this.insertInnerItem(item, this.container.innerIndexOf(item));
        } else {
            this.removeInnerItem(item);
        }
    },
    insertInnerItem: function(item, index) {
        var container = this.container,
            containerDom = container.innerElement.dom,
            itemDom = item.element.dom,
            nextSibling = index !== -1 ? container.getInnerAt(index + 1) : null,
            nextSiblingDom = null,
            translatable;
        if (nextSibling) {
            translatable = nextSibling.getTranslatable();
            if (translatable && translatable.getUseWrapper()) {
                nextSiblingDom = translatable.getWrapper().dom;
            } else {
                nextSiblingDom = nextSibling ? nextSibling.element.dom : null;
            }
        }
        containerDom.insertBefore(itemDom, nextSiblingDom);
        return this;
    },
    insertBodyItem: function(item) {
        var container = this.container.setUseBodyElement(true),
            bodyDom = container.bodyElement.dom;
        if (item.getZIndex() === null) {
            item.setZIndex((container.indexOf(item) + 1) * 2);
        }
        bodyDom.insertBefore(item.element.dom, bodyDom.firstChild);
        return this;
    },
    removeInnerItem: function(item) {
        item.element.detach();
    },
    removeBodyItem: function(item) {
        item.setZIndex(null);
        item.element.detach();
    },
    onItemRemove: function(item, index, destroying) {
        var docked = item.getDocked();
        if (docked) {
            this.undockItem(item);
        } else if (item.isCentered()) {
            this.onItemCenteredChange(item, false);
        } else if (item.isFloating()) {
            this.onItemFloatingChange(item, false);
        } else {
            this.onItemInnerStateChange(item, false, destroying);
        }
    },
    onItemMove: function(item, toIndex, fromIndex) {
        if (item.isCentered() || item.isFloating()) {
            item.setZIndex((toIndex + 1) * 2);
        } else if (item.isInnerItem()) {
            this.insertInnerItem(item, this.container.innerIndexOf(item));
        } else {
            this.undockItem(item);
            this.dockItem(item);
        }
    },
    onItemCenteredChange: function(item, centered) {
        var wrapperName = '$centerWrapper';
        if (centered) {
            this.insertBodyItem(item);
            item.link(wrapperName, new Ext.util.Wrapper({
                className: this.centerWrapperClass
            }, item.element));
        } else {
            item.unlink([
                wrapperName
            ]);
            this.removeBodyItem(item);
        }
    },
    onItemFloatingChange: function(item, floating) {
        if (floating) {
            this.insertBodyItem(item);
        } else {
            this.removeBodyItem(item);
        }
    },
    onAfterItemDockedChange: function(item, docked, oldDocked) {
        
        
        if (item.initialized) {
            if (oldDocked) {
                this.undockItem(item);
            }
            if (docked) {
                this.dockItem(item);
            }
        }
    },
    onContainerSizeStateChange: function() {
        var dockWrapper = this.getDockWrapper();
        if (dockWrapper) {
            dockWrapper.setSizeState(this.container.getSizeState());
        }
    },
    onContainerSizeFlagsChange: function() {
        var items = this.dockedItems,
            i, ln, item;
        for (i = 0 , ln = items.length; i < ln; i++) {
            item = items[i];
            this.refreshDockedItemLayoutSizeFlags(item);
        }
    },
    refreshDockedItemLayoutSizeFlags: function(item) {
        var container = this.container,
            dockedDirection = this.positionDirectionMap[item.getDocked()],
            binaryMask = (dockedDirection === 'horizontal') ? container.LAYOUT_HEIGHT : container.LAYOUT_WIDTH,
            flags = (container.getSizeFlags() & binaryMask);
        item.setLayoutSizeFlags(flags);
    },
    dockItem: function(item) {
        var me = this,
            DockClass = Ext.layout.wrapper.BoxDock,
            dockedItems = me.dockedItems,
            ln = dockedItems.length,
            container = me.container,
            itemIndex = container.indexOf(item),
            positionDirectionMap = me.positionDirectionMap,
            direction = positionDirectionMap[item.getDocked()],
            dockInnerWrapper = me.dockInnerWrapper,
            referenceDirection, i, dockedItem, index, previousItem, slice, referenceItem, referenceDocked, referenceWrapper, newWrapper, nestedWrapper, oldInnerWrapper;
        me.monitorSizeStateChange();
        me.monitorSizeFlagsChange();
        if (!dockInnerWrapper) {
            dockInnerWrapper = me.link('dockInnerWrapper', new Ext.layout.wrapper.Inner({
                container: container
            }));
        }
        if (ln === 0) {
            dockedItems.push(item);
            newWrapper = new DockClass({
                container: container,
                direction: direction
            });
            newWrapper.addItem(item);
            newWrapper.getElement().replace(dockInnerWrapper.getElement(), false);
            newWrapper.setInnerWrapper(dockInnerWrapper);
            container.onInitialized('onContainerSizeStateChange', me);
        } 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 DockClass({
                    container: container,
                    direction: direction
                });
                if (slice.length > 0) {
                    if (slice.length === referenceWrapper.itemsCount) {
                        nestedWrapper = referenceWrapper;
                        newWrapper.setSizeState(nestedWrapper.getSizeState());
                        newWrapper.getElement().replace(nestedWrapper.getElement(), false);
                    } else {
                        nestedWrapper = new DockClass({
                            container: container,
                            direction: referenceDirection
                        });
                        nestedWrapper.setInnerWrapper(referenceWrapper.getInnerWrapper());
                        nestedWrapper.addItems(slice);
                        referenceWrapper.setInnerWrapper(newWrapper);
                    }
                    newWrapper.setInnerWrapper(nestedWrapper);
                } else {
                    oldInnerWrapper = referenceWrapper.getInnerWrapper();
                    referenceWrapper.setInnerWrapper(null);
                    newWrapper.setInnerWrapper(oldInnerWrapper);
                    referenceWrapper.setInnerWrapper(newWrapper);
                }
                newWrapper.addItem(item);
            }
        }
        if (container.initialized) {
            me.handleDockedItemBorders();
        }
        container.onInitialized('refreshDockedItemLayoutSizeFlags', me, [
            item
        ]);
    },
    getDockWrapper: function() {
        var dockedItems = this.dockedItems;
        if (dockedItems.length > 0) {
            return dockedItems[0].$dockWrapper;
        }
        return null;
    },
    undockItem: function(item) {
        var me = this,
            dockedItems = me.dockedItems,
            lastBorderMask, lastBorderCollapse;
        if (item.$dockWrapper) {
            item.$dockWrapper.removeItem(item);
        }
        if (me.container.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);
        item.setLayoutSizeFlags(0);
    },
    destroy: function() {
        this.dockedItems = null;
        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() {
        var me = this,
            edges = 0,
            maskT = 8,
            maskR = 4,
            maskB = 2,
            maskL = 1,
            container = me.container,
            bodyBorder = container.getBoodyBorder && container.getBodyBorder(),
            containerBorder = container.getBorder(),
            collapsed = me.collapsed,
            edgeMasks = me.edgeMasks,
            noBorderCls = me.noBorderClassTable,
            dockedItemsGen = container.items.generation,
            b, borderCls, docked, edgesTouched, i, ln, item, dock, lastValue, mask, addCls, removeCls;
        if (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;
        for (i = 0 , ln = docked.length; i < ln; i++) {
            item = docked[i];
            dock = 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[removeCls.length] = borderCls[lastValue];
                }
                if (edgesTouched) {
                    addCls[addCls.length] = 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[removeCls.length] = borderCls[lastValue];
            }
            if (edgesTouched) {
                addCls[addCls.length] = borderCls[edgesTouched];
            }
        }
        if (removeCls.length && container.removeBodyCls) {
            container.removeBodyCls(removeCls);
        }
        if (addCls.length && container.addBodyCls) {
            container.addBodyCls(addCls);
        }
    },
    
    borderCollapseMap: {},
    
    
    getBorderCollapseTable: function() {
        var me = this,
            map = me.borderCollapseMap,
            container = me.container,
            baseCls = container.getBaseCls(),
            ui = container.ui,
            uiCls = (ui ? ('-' + ui) : ''),
            table;
        ui = ui || 'default';
        map = map[baseCls] || (map[baseCls] = {});
        table = map[ui];
        if (!table) {
            baseCls += uiCls + '-outer-border-';
            map[ui] = table = [
                0,
                
                baseCls + 'l',
                
                baseCls + 'b',
                
                baseCls + 'bl',
                
                baseCls + 'r',
                
                baseCls + 'rl',
                
                baseCls + 'rb',
                
                baseCls + 'rbl',
                
                baseCls + 't',
                
                baseCls + 'tl',
                
                baseCls + 'tb',
                
                baseCls + 'tbl',
                
                baseCls + 'tr',
                
                baseCls + 'trl',
                
                baseCls + 'trb',
                
                baseCls + 'trbl'
            ];
        }
        
        return table;
    }
});


Ext.define('Ext.layout.Box', {
    extend: Ext.layout.Default,
    config: {
        orient: 'horizontal',
        
        align: 'start',
        
        pack: 'start'
    },
    alias: 'layout.tablebox',
    layoutBaseClass: 'x-layout-tablebox',
    itemClass: 'x-layout-tablebox-item',
    setContainer: function(container) {
        this.callParent([
            container
        ]);
        container.innerElement.addCls(this.layoutBaseClass);
        container.on('flexchange', 'onItemFlexChange', this, {
            delegate: '> component'
        });
    },
    onItemInnerStateChange: function(item, isInner) {
        this.callParent(arguments);
        item.toggleCls(this.itemClass, isInner);
    },
    onItemFlexChange: Ext.emptyFn
});


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


Ext.define('Ext.fx.animation.Abstract', {
    extend: Ext.Evented,
    isAnimation: true,
    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_DOWN: 'down',
    DIRECTION_LEFT: 'left',
    DIRECTION_RIGHT: 'right',
    stateNameRegex: /^(?:[\d\.]+)%$/,
    constructor: function() {
        this.states = {};
        this.callParent(arguments);
        return this;
    },
    applyElement: function(element) {
        return Ext.get(element);
    },
    applyBefore: function(before, current) {
        if (before) {
            return Ext.factory(before, Ext.fx.State, current);
        }
    },
    applyAfter: function(after, current) {
        if (after) {
            return Ext.factory(after, Ext.fx.State, current);
        }
    },
    setFrom: function(from) {
        return this.setState(this.STATE_FROM, from);
    },
    setTo: function(to) {
        return this.setState(this.STATE_TO, to);
    },
    getFrom: function() {
        return this.getState(this.STATE_FROM);
    },
    getTo: function() {
        return this.getState(this.STATE_TO);
    },
    setStates: function(states) {
        var validNameRegex = this.stateNameRegex,
            name;
        for (name in states) {
            if (validNameRegex.test(name)) {
                this.setState(name, states[name]);
            }
        }
        return this;
    },
    getStates: function() {
        return this.states;
    },
    updateCallback: function(callback) {
        if (callback) {
            this.setOnEnd(callback);
        }
    },
    end: function() {
        
        
        this.stop();
    },
    stop: function() {
        this.fireEvent('stop', this);
    },
    destroy: function() {
        this.stop();
        this.callParent();
    },
    setState: function(name, state) {
        var states = this.getStates(),
            stateInstance;
        stateInstance = Ext.factory(state, Ext.fx.State, states[name]);
        if (stateInstance) {
            states[name] = stateInstance;
        }
        
        else if (name === this.STATE_TO) {
            Ext.Logger.error("Setting and invalid '100%' / 'to' state of: " + state);
        }
        
        return this;
    },
    getState: function(name) {
        return this.getStates()[name];
    },
    getData: function() {
        var 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',
        down: 'up',
        left: 'right',
        right: 'left'
    },
    applyEasing: function(easing) {
        if (easing === 'auto') {
            return 'ease-' + ((this.getOut()) ? 'in' : 'out');
        }
        return easing;
    },
    getContainerBox: function() {
        var box = this._containerBox;
        if (box === 'auto') {
            box = this.getElement().getParent().getBox();
        }
        return box;
    },
    getElementBox: function() {
        var box = this._elementBox;
        if (this.getIsElementBoxFit()) {
            return this.getContainerBox();
        }
        if (box === 'auto') {
            box = this.getElement().getBox();
        }
        return box;
    },
    getData: function() {
        var elementBox = this.getElementBox(),
            containerBox = this.getContainerBox(),
            box = elementBox ? elementBox : containerBox,
            from = this.getFrom(),
            to = this.getTo(),
            out = this.getOut(),
            offset = this.getOffset(),
            direction = this.getDirection(),
            useCssTransform = this.getUseCssTransform(),
            reverse = this.getReverse(),
            translateX = 0,
            translateY = 0,
            fromX, fromY, toX, toY;
        if (reverse) {
            direction = this.reverseDirectionMap[direction];
        }
        switch (direction) {
            case this.DIRECTION_UP:
                if (out) {
                    translateY = containerBox.top - box.top - box.height - offset;
                } else {
                    translateY = containerBox.bottom - box.bottom + box.height + offset;
                };
                break;
            case this.DIRECTION_DOWN:
                if (out) {
                    translateY = containerBox.bottom - box.bottom + box.height + offset;
                } else {
                    translateY = containerBox.top - box.height - box.top - offset;
                };
                break;
            case this.DIRECTION_RIGHT:
                if (out) {
                    translateX = containerBox.right - box.right + box.width + offset;
                } else {
                    translateX = containerBox.left - box.left - box.width - offset;
                };
                break;
            case this.DIRECTION_LEFT:
                if (out) {
                    translateX = containerBox.left - box.left - box.width - offset;
                } else {
                    translateX = containerBox.right - box.right + box.width + offset;
                };
                break;
        }
        fromX = (out) ? 0 : translateX;
        fromY = (out) ? 0 : translateY;
        if (useCssTransform) {
            from.setTransform({
                translateX: fromX,
                translateY: fromY
            });
        } else {
            from.set('left', fromX);
            from.set('top', fromY);
        }
        toX = (out) ? translateX : 0;
        toY = (out) ? translateY : 0;
        if (useCssTransform) {
            to.setTransform({
                translateX: toX,
                translateY: toY
            });
        } else {
            to.set('left', toX);
            to.set('top', toY);
        }
        return this.callParent(arguments);
    }
});


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


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


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


Ext.define('Ext.fx.animation.Flip', {
    extend: Ext.fx.animation.Abstract,
    alias: 'animation.flip',
    config: {
        easing: 'ease-in',
        
        direction: 'right',
        half: false,
        out: null
    },
    getData: function() {
        var 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:
                if (out) {
                    toRotateX = rotate;
                } else {
                    fromRotateX = -rotate;
                };
                break;
            case this.DIRECTION_DOWN:
                if (out) {
                    toRotateX = -rotate;
                } else {
                    fromRotateX = rotate;
                };
                break;
            case this.DIRECTION_RIGHT:
                if (out) {
                    toRotateY = rotate;
                } else {
                    fromRotateY = -rotate;
                };
                break;
            case this.DIRECTION_LEFT:
                if (out) {
                    toRotateY = -rotate;
                } else {
                    fromRotateY = rotate;
                };
                break;
        }
        from.setTransform({
            rotateX: fromRotateX,
            rotateY: fromRotateY,
            scale: fromScale
        });
        to.setTransform({
            rotateX: toRotateX,
            rotateY: toRotateY,
            scale: toScale
        });
        return this.callParent();
    }
});


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


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


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


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


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


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


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


Ext.define('Ext.fx.layout.card.Scroll', {
    extend: Ext.fx.layout.card.Abstract,
    alias: 'fx.layout.card.scroll',
    config: {
        duration: 150
    },
    constructor: function(config) {
        this.initConfig(config);
    },
    getEasing: function() {
        var easing = this.easing;
        if (!easing) {
            this.easing = easing = new Ext.fx.easing.Linear();
        }
        return easing;
    },
    updateDuration: function(duration) {
        this.getEasing().setDuration(duration);
    },
    onActiveItemChange: function(cardLayout, newItem, oldItem, controller) {
        var direction = this.getDirection(),
            easing = this.getEasing(),
            containerElement, inElement, outElement, containerWidth, containerHeight, reverse;
        if (newItem && oldItem) {
            if (this.isAnimating) {
                this.stopAnimation();
            }
            newItem.setWidth('100%');
            newItem.setHeight('100%');
            containerElement = this.getLayout().container.innerElement;
            containerWidth = containerElement.getWidth();
            containerHeight = containerElement.getHeight();
            inElement = newItem.renderElement;
            outElement = oldItem.renderElement;
            this.oldItem = oldItem;
            this.newItem = newItem;
            this.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.fx.layout.Card', {
    constructor: function(config) {
        var defaultClass = Ext.fx.layout.card.Abstract,
            type;
        if (!config) {
            return null;
        }
        if (typeof config == 'string') {
            type = config;
            config = {};
        } else if (config.type) {
            type = config.type;
        }
        config.elementBox = false;
        if (type) {
            defaultClass = Ext.ClassManager.getByAlias('fx.layout.card.' + type);
            
            if (!defaultClass) {
                Ext.Logger.error("Unknown card animation type: '" + type + "'");
            }
        }
        
        return Ext.factory(config, defaultClass);
    }
});


Ext.define('Ext.layout.Card', {
    extend: Ext.layout.Default,
    alias: 'layout.card',
    isCard: true,
    
    layoutClass: Ext.baseCSSPrefix + 'layout-card',
    itemClass: Ext.baseCSSPrefix + 'layout-card-item',
    
    applyAnimation: function(animation) {
        return new Ext.fx.layout.Card(animation);
    },
    
    updateAnimation: function(animation, oldAnimation) {
        if (animation && animation.isAnimation) {
            animation.setLayout(this);
        }
        if (oldAnimation) {
            oldAnimation.destroy();
        }
    },
    setContainer: function(container) {
        this.callParent(arguments);
        container.innerElement.addCls(this.layoutClass);
        container.onInitialized('onContainerInitialized', this);
    },
    onContainerInitialized: function() {
        var me = this,
            container = me.container,
            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) {
        this.fireEventedAction('activeitemchange', [
            this,
            newItem,
            oldItem
        ], 'doActiveItemChange', this);
    },
    onItemInnerStateChange: function(item, isInner, destroying) {
        this.callParent(arguments);
        var container = this.container,
            activeItem = container.getActiveItem();
        item.toggleCls(this.itemClass, isInner);
        item.setLayoutSizeFlags(isInner ? container.LAYOUT_BOTH : 0);
        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) {
        if (oldActiveItem) {
            oldActiveItem.hide();
        }
        if (newActiveItem) {
            newActiveItem.show();
        }
    },
    destroy: function() {
        this.callParent();
        Ext.destroy(this.getAnimation());
    }
});


Ext.define('Ext.layout.Fit', {
    extend: Ext.layout.Default,
    isFit: true,
    alias: 'layout.fit',
    layoutClass: 'x-layout-fit',
    itemClass: 'x-layout-fit-item',
    setContainer: function(container) {
        this.callParent(arguments);
        container.innerElement.addCls(this.layoutClass);
        this.onContainerSizeFlagsChange();
        this.monitorSizeFlagsChange();
    },
    onContainerSizeFlagsChange: function() {
        var container = this.container,
            sizeFlags = container.getSizeFlags(),
            stretched = Boolean(sizeFlags & container.LAYOUT_STRETCHED),
            innerItems = container.innerItems,
            i, ln, item;
        this.callParent();
        for (i = 0 , ln = innerItems.length; i < ln; i++) {
            item = innerItems[i];
            item.setLayoutSizeFlags(sizeFlags);
        }
        container.innerElement.toggleCls('x-stretched', stretched);
    },
    onItemInnerStateChange: function(item, isInner) {
        this.callParent(arguments);
        item.toggleCls(this.itemClass, isInner);
        item.setLayoutSizeFlags(isInner ? this.container.getSizeFlags() : 0);
    }
});


Ext.define('Ext.layout.FlexBox', {
    extend: Ext.layout.Box,
    alias: 'layout.box',
    config: {
        align: 'stretch'
    },
    layoutBaseClass: 'x-layout-box',
    itemClass: 'x-layout-box-item',
    setContainer: function(container) {
        this.callParent(arguments);
        this.monitorSizeFlagsChange();
    },
    applyOrient: function(orient) {
        
        if (orient !== 'horizontal' && orient !== 'vertical') {
            Ext.Logger.error("Invalid box orient of: '" + orient + "', must be either 'horizontal' or 'vertical'");
        }
        
        return orient;
    },
    updateOrient: function(orient, oldOrient) {
        var container = this.container,
            delegation = {
                delegate: '> component'
            };
        if (orient === 'horizontal') {
            this.sizePropertyName = 'width';
        } else {
            this.sizePropertyName = 'height';
        }
        container.innerElement.swapCls('x-' + orient, 'x-' + oldOrient);
        if (oldOrient) {
            container.un(oldOrient === 'horizontal' ? 'widthchange' : 'heightchange', 'onItemSizeChange', this, delegation);
            this.redrawContainer();
        }
        container.on(orient === 'horizontal' ? 'widthchange' : 'heightchange', 'onItemSizeChange', this, delegation);
    },
    onItemInnerStateChange: function(item, isInner) {
        this.callParent(arguments);
        var flex, size;
        item.toggleCls(this.itemClass, isInner);
        if (isInner) {
            flex = item.getFlex();
            size = item.getConfig(this.sizePropertyName);
            if (flex) {
                this.doItemFlexChange(item, flex);
            } else if (size) {
                this.doItemSizeChange(item, size);
            }
        }
        this.refreshItemSizeState(item);
    },
    refreshItemSizeState: function(item) {
        var isInner = item.isInnerItem(),
            container = this.container,
            LAYOUT_HEIGHT = container.LAYOUT_HEIGHT,
            LAYOUT_WIDTH = container.LAYOUT_WIDTH,
            dimension = this.sizePropertyName,
            layoutSizeFlags = 0,
            containerSizeFlags = container.getSizeFlags();
        if (isInner) {
            layoutSizeFlags |= container.LAYOUT_STRETCHED;
            if (this.getAlign() === 'stretch') {
                layoutSizeFlags |= containerSizeFlags & (dimension === 'width' ? LAYOUT_HEIGHT : LAYOUT_WIDTH);
            }
            if (item.getFlex()) {
                layoutSizeFlags |= containerSizeFlags & (dimension === 'width' ? LAYOUT_WIDTH : LAYOUT_HEIGHT);
            }
        }
        item.setLayoutSizeFlags(layoutSizeFlags);
    },
    refreshAllItemSizedStates: function() {
        var innerItems = this.container.innerItems,
            i, ln, item;
        for (i = 0 , ln = innerItems.length; i < ln; i++) {
            item = innerItems[i];
            this.refreshItemSizeState(item);
        }
    },
    onContainerSizeFlagsChange: function() {
        this.refreshAllItemSizedStates();
        this.callParent(arguments);
    },
    onItemSizeChange: function(item, size) {
        if (item.isInnerItem()) {
            this.doItemSizeChange(item, size);
        }
    },
    doItemSizeChange: function(item, size) {
        if (size) {
            item.setFlex(null);
            this.redrawContainer();
        }
    },
    onItemFlexChange: function(item, flex) {
        if (item.isInnerItem()) {
            this.doItemFlexChange(item, flex);
            this.refreshItemSizeState(item);
        }
    },
    doItemFlexChange: function(item, flex) {
        this.setItemFlex(item, flex);
        if (flex) {
            item.setConfig(this.sizePropertyName, null);
        } else {
            this.redrawContainer();
        }
    },
    redrawContainer: function() {
        var container = this.container,
            renderedTo = container.element.dom.parentNode;
        if (renderedTo && renderedTo.nodeType !== 11) {
            container.innerElement.redraw();
        }
    },
    
    setItemFlex: function(item, flex) {
        var element = item.element,
            style = element.dom.style;
        element.toggleCls(Ext.baseCSSPrefix + 'flexed', !!flex);
        flex = flex ? String(flex) : '';
        if (Ext.browser.is.WebKit) {
            style.setProperty('-webkit-box-flex', flex, null);
        } else if (Ext.browser.is.IE) {
            style.setProperty('-ms-flex', flex + ' 0 0px', null);
        } else {
            style.setProperty('flex', flex + ' 0 0px', null);
        }
    },
    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) {
        var container = this.container;
        container.innerElement.swapCls(align, oldAlign, true, 'x-align');
        if (oldAlign !== undefined) {
            this.refreshAllItemSizedStates();
        }
    },
    applyPack: function(pack) {
        return this.convertPosition(pack);
    },
    updatePack: function(pack, oldPack) {
        this.container.innerElement.swapCls(pack, oldPack, true, 'x-pack');
    }
});


Ext.define('Ext.layout.Float', {
    extend: Ext.layout.Default,
    alias: 'layout.float',
    config: {
        direction: 'left'
    },
    layoutClass: 'layout-float',
    itemClass: 'layout-float-item',
    setContainer: function(container) {
        this.callParent(arguments);
        container.innerElement.addCls(this.layoutClass);
    },
    onItemInnerStateChange: function(item, isInner) {
        this.callParent(arguments);
        item.toggleCls(this.itemClass, isInner);
    },
    updateDirection: function(direction, oldDirection) {
        var prefix = 'direction-';
        this.container.innerElement.swapCls(prefix + direction, prefix + oldDirection);
    }
});


Ext.define('Ext.layout.HBox', {
    extend: Ext.layout.FlexBox,
    alias: 'layout.hbox'
});


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


Ext.define('Ext.layout.wrapper.Dock', {
    config: {
        direction: 'horizontal',
        element: {
            className: 'x-dock'
        },
        bodyElement: {
            className: 'x-dock-body'
        },
        innerWrapper: null,
        sizeState: false,
        container: null
    },
    positionMap: {
        top: 'start',
        left: 'start',
        bottom: 'end',
        right: 'end'
    },
    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),
            items = this.items,
            sideItems = items[position],
            itemWrapper, element, i, ln, sibling, referenceElement, siblingIndex;
        if (wrapper) {
            wrapper.removeItem(item);
        }
        item.$dockWrapper = this;
        itemWrapper = item.link('$dockItemWrapper', new Ext.util.Wrapper({
            className: 'x-dock-item'
        }));
        item.addCls('x-docked-' + docked);
        element = itemWrapper.element;
        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.getBodyElement();
        }
        this.itemsCount++;
        if (position === 'start') {
            element.insertBefore(referenceElement);
        } else {
            element.insertAfter(referenceElement);
        }
        itemWrapper.wrap(item.element);
        itemWrapper.bindSize(this.getDirection() === 'horizontal' ? 'width' : 'height');
    },
    removeItem: function(item) {
        var position = item.getDocked(),
            items = this.items[this.positionMap[position]];
        item.removeCls('x-docked-' + position);
        Ext.Array.remove(items, item);
        item.unlink([
            '$dockItemWrapper'
        ]);
        item.element.detach();
        delete item.$dockWrapper;
        if (--this.itemsCount === 0) {
            this.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('x-dock-' + this.getDirection());
    },
    applyBodyElement: function(bodyElement) {
        return Ext.Element.create(bodyElement);
    },
    updateBodyElement: function(bodyElement) {
        this.getElement().append(bodyElement);
    },
    updateInnerWrapper: function(innerWrapper, oldInnerWrapper) {
        var innerElement = this.getBodyElement();
        if (oldInnerWrapper && oldInnerWrapper.$outerWrapper === this) {
            innerElement.remove(oldInnerWrapper.getElement());
            delete oldInnerWrapper.$outerWrapper;
        }
        if (innerWrapper) {
            innerWrapper.setSizeState(this.getSizeState());
            innerWrapper.$outerWrapper = this;
            innerElement.append(innerWrapper.getElement());
        }
    },
    updateSizeState: function(state) {
        var innerWrapper = this.getInnerWrapper();
        this.getElement().setSizeState(state);
        if (innerWrapper) {
            innerWrapper.setSizeState(state);
        }
    },
    destroy: function() {
        var me = this,
            innerWrapper = me.getInnerWrapper(),
            outerWrapper = me.$outerWrapper;
        if (innerWrapper) {
            if (outerWrapper) {
                outerWrapper.setInnerWrapper(innerWrapper);
            } else {
                innerWrapper.getElement().replace(me.getElement());
                delete innerWrapper.$outerWrapper;
            }
        }
        delete me.$outerWrapper;
        me.setInnerWrapper(null);
        me.unlink([
            '_bodyElement',
            '_element'
        ]);
        me.callParent();
    }
});


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.util.InputBlocker', {
    singleton: true,
    blockInputs: function() {
        if (Ext.browser.is.ie) {
            Ext.select('.x-field-text .x-field-input:not(.x-item-disabled) .x-input-el, .x-field-textarea .x-field-input:not(.x-item-disabled) .x-input-el, .x-field-search .x-field-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.Mask', {
    extend: Ext.Component,
    xtype: 'mask',
    config: {
        
        baseCls: Ext.baseCSSPrefix + 'mask',
        
        transparent: false,
        
        top: 0,
        
        left: 0,
        
        right: 0,
        
        bottom: 0
    },
    
    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.getBaseCls() + '-transparent', transparent);
    }
});


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,
    config: {
        
        referenceHolder: false
    },
    
    getReferences: function() {
        Ext.ComponentManager.fixReferences();
        return this.refs || null;
    },
    
    lookup: function(key) {
        var refs = this.getReferences();
        return (refs && refs[key]) || null;
    },
    
    lookupReference: function(key) {
        return this.lookup(key);
    },
    privates: {
        
        attachReference: function(component) {
            var me = this,
                key, refs;
            
            if (me.destroying || me.destroyed) {
                return;
            }
            refs = me.refs || (me.refs = {});
            key = component.referenceKey;
            
            if (refs[key] && refs[key] !== component) {
                Ext.log.warn('Duplicate reference: "' + key + '" on ' + me.id);
            }
            
            refs[key] = component;
        },
        
        clearReference: function(component) {
            var refs = this.refs,
                key = component.referenceKey;
            if (refs && key) {
                
                
                
                component.viewModelKey = component.referenceKey = refs[key] = null;
            }
        },
        containerOnAdded: function(component, instanced) {
            
            
            
            if (instanced) {
                Ext.ComponentManager.markReferencesDirty();
            }
        },
        containerOnRemoved: function(destroying) {
            var refHolder;
            
            if (!destroying) {
                refHolder = this.lookupReferenceHolder();
                if (refHolder) {
                    
                    
                    
                    
                    Ext.ComponentManager.markReferencesDirty();
                    refHolder.clearReferences();
                }
            }
        },
        
        clearReferences: function() {
            this.refs = null;
        },
        initContainerInheritedState: function(inheritedState, inheritedStateInner) {
            var me = this,
                controller = me.getController(),
                session = me.getSession(),
                
                
                viewModel = me.getConfig('viewModel', true),
                reference = me.getReference(),
                referenceHolder = me.getReferenceHolder();
            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.Container', {
    extend: Ext.Component,
    alternateClassName: [
        'Ext.lib.Container',
        'Ext.container.Container'
    ],
    xtype: 'container',
    mixins: [
        Ext.mixin.Queryable,
        Ext.mixin.Container
    ],
    
    
    
    
    
    
    eventedConfig: {
        
        activeItem: 0
    },
    config: {
        
        
        layout: 'default',
        
        control: {},
        
        defaults: null,
        
        
        items: null,
        
        autoDestroy: true,
        
        defaultType: null,
        
        masked: null,
        
        modal: null,
        
        hideOnMaskTap: null
    },
    
    manageBorders: false,
    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;
        if (me.manageBorders) {
            me.element.addCls('x-managed-borders');
        }
    },
    initialize: function() {
        this.callParent();
        
        
        
        this.getLayout();
    },
    getElementConfig: function() {
        return {
            reference: 'element',
            classList: [
                'x-container',
                'x-unsized'
            ],
            children: [
                {
                    reference: 'innerElement',
                    className: 'x-inner'
                }
            ]
        };
    },
    
    applyMasked: function(masked) {
        var isVisible = true,
            currentMask;
        if (masked === false) {
            masked = true;
            isVisible = false;
        }
        currentMask = Ext.factory(masked, Ext.Mask, this.getMasked());
        if (currentMask) {
            this.add(currentMask);
            currentMask.setHidden(!isVisible);
        }
        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) {
        var me = this,
            modal;
        me.callParent([
            parent,
            instanced
        ]);
        me.containerOnAdded(parent, instanced);
        modal = me.getModal();
        if (modal) {
            parent.insertBefore(modal, me);
            modal.setZIndex(me.getZIndex() - 1);
        }
    },
    onRemoved: function(destroying) {
        this.containerOnRemoved(destroying);
        this.callParent([
            destroying
        ]);
    },
    applyModal: function(modal, currentModal) {
        var isVisible = true;
        if (modal === false) {
            modal = true;
            isVisible = false;
        }
        currentModal = Ext.factory(modal, Ext.Mask, currentModal);
        if (currentModal) {
            currentModal.setVisibility(isVisible);
        }
        return currentModal;
    },
    updateModal: function(modal) {
        var container = this.getParent();
        if (container) {
            if (modal) {
                container.insertBefore(modal, this);
                modal.setZIndex(this.getZIndex() - 1);
            } else {
                container.remove(modal);
            }
        }
    },
    updateHideOnMaskTap: function(hide) {
        var mask = this.getModal();
        if (mask) {
            mask[hide ? 'on' : 'un'].call(mask, 'tap', 'hide', this);
        }
    },
    updateZIndex: function(zIndex) {
        var modal = this.getModal();
        this.callParent(arguments);
        if (modal) {
            modal.setZIndex(zIndex - 1);
        }
    },
    updateBaseCls: function(newBaseCls, oldBaseCls) {
        var me = this,
            element = me.element,
            ui = me.getUi();
        if (oldBaseCls) {
            element.removeCls(oldBaseCls);
            me.innerElement.removeCls(newBaseCls, null, 'inner');
            if (ui) {
                element.removeCls(me.currentUi);
            }
        }
        if (newBaseCls) {
            element.addCls(newBaseCls);
            me.innerElement.addCls(newBaseCls, null, 'inner');
            if (ui) {
                element.addCls(newBaseCls, null, ui);
                me.currentUi = newBaseCls + '-' + ui;
            }
        }
    },
    applyItems: function(items, collection) {
        if (items) {
            var me = this,
                activeItem;
            me.getDefaultType();
            me.getDefaults();
            if (me.initialized && collection.length > 0) {
                me.removeAll();
            }
            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;
    },
    
    onFirstItemAdd: function() {
        var me = this;
        delete me.onItemAdd;
        if (me.innerHtmlElement && !me.getHtml()) {
            me.innerHtmlElement.destroy();
            delete me.innerHtmlElement;
        }
        me.on('innerstatechange', 'onItemInnerStateChange', me, {
            delegate: '> component'
        });
        return me.onItemAdd.apply(me, arguments);
    },
    
    updateLayout: function(newLayout, oldLayout) {
        
        if (!oldLayout || !oldLayout.isLayout) {
            return;
        }
        if (!oldLayout.isCompatible(newLayout)) {
            Ext.Logger.error('Replacing a layout after one has already been initialized is not supported. ' + this.$className + '#' + this.getId() + ' (' + oldLayout.$className + ' / ' + (Ext.isString(newLayout) ? newLayout : JSON.stringify(newLayout)) + ')');
        }
    },
    
    getLayout: function() {
        var layout = this.layout;
        if (!(layout && layout.isLayout)) {
            layout = this.link('_layout', this.link('layout', Ext.factory(this._layout || 'default', Ext.layout.Default, null, 'layout')));
            layout.setContainer(this);
        }
        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,
            defaults = me.getDefaults(),
            instance;
        
        if (item.isComponent) {
            instance = item;
            
            if (defaults && item.isInnerItem() && !me.has(instance)) {
                instance.setConfig(defaults, true);
            }
        } else 
        {
            if (defaults && !item.ignoreDefaults) {
                
                
                
                if (!(item.hasOwnProperty('left') && item.hasOwnProperty('right') && item.hasOwnProperty('top') && item.hasOwnProperty('bottom') && item.hasOwnProperty('docked') && item.hasOwnProperty('centered'))) {
                    item = Ext.mergeIf({}, item, defaults);
                }
            }
            
            
            if (!me.$hasCachedDefaultItemClass) {
                me.getDefaultType();
                me.$hasCachedDefaultItemClass = true;
            }
            instance = Ext.factory(item, me.defaultItemClass);
        }
        return instance;
    },
    
    add: function(newItems) {
        var me = this,
            addingArray = true,
            addedItems = [],
            i, ln, item, newActiveItem, instanced;
        if (!Ext.isArray(newItems)) {
            newItems = [
                newItems
            ];
            addingArray = false;
        }
        for (i = 0 , ln = newItems.length; i < ln; i++) {
            item = newItems[i];
            instanced = item.isWidget;
            if (!instanced) {
                item.$initParent = me;
            }
            item = me.factoryItem(item);
            me.doAdd(item, instanced);
            delete item.$initParent;
            if (!newActiveItem && !me.getActiveItem() && me.innerItems.length > 0 && item.isInnerItem()) {
                newActiveItem = item;
            }
            addedItems.push(item);
        }
        if (newActiveItem) {
            me.setActiveItem(newActiveItem);
        }
        return addingArray ? addedItems : addedItems[0];
    },
    
    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);
            }
            item.onAdded(me, !!instanced);
            me.onItemAdd(item, index);
        }
    },
    
    remove: function(item, destroy) {
        var me = this,
            index, innerItems;
        item = me.getComponent(item);
        index = me.indexOf(item);
        innerItems = me.getInnerItems();
        if (destroy === undefined) {
            destroy = me.getAutoDestroy();
        }
        if (index !== -1) {
            if (!me.removingAll && innerItems.length > 1 && item === me.getActiveItem()) {
                me.on({
                    activeitemchange: 'doRemove',
                    scope: me,
                    single: true,
                    order: 'after',
                    args: [
                        item,
                        index,
                        destroy
                    ]
                });
                me.doResetActiveItem(innerItems.indexOf(item));
            } else {
                me.doRemove(item, index, destroy);
                if (innerItems.length === 0) {
                    me.setActiveItem(null);
                }
            }
        }
        return item;
    },
    doResetActiveItem: function(innerIndex) {
        if (innerIndex === 0) {
            this.setActiveItem(1);
        } else {
            this.setActiveItem(0);
        }
    },
    doRemove: function(item, index, destroy) {
        var me = this;
        me.items.remove(item);
        if (item.isInnerItem()) {
            me.removeInner(item);
        }
        me.onItemRemove(item, index, destroy);
        item.onRemoved(item.destroying || destroy);
        if (destroy) {
            item.destroy();
        }
    },
    
    removeAll: function(destroy, everything) {
        var items = this.items,
            removed = [],
            ln = items.length,
            i = 0,
            item;
        if (typeof destroy != 'boolean') {
            destroy = this.getAutoDestroy();
        }
        everything = Boolean(everything);
        
        this.removingAll = true;
        for (; i < ln; i++) {
            item = items.getAt(i);
            if (item && (everything || item.isInnerItem())) {
                this.doRemove(item, i, destroy);
                i--;
                ln--;
            }
            removed.push(item);
        }
        this.setActiveItem(null);
        this.removingAll = false;
        return removed;
    },
    
    getAt: function(index) {
        return this.items.getAt(index);
    },
    getInnerAt: function(index) {
        return this.innerItems[index];
    },
    
    removeAt: function(index) {
        var item = this.getAt(index);
        if (item) {
            this.remove(item);
        }
        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 me;
        }
        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.isRendered() && item.setRendered(true)) {
            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 (this.isRendered() && 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;
        if (item.isDocked()) {
            item.setDocked(null);
        }
        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);
    },
    
    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();
        
        me.getItems();
        
        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);
                activeItem = {
                    xtype: activeItem
                };
            }
            if (!item || !item.isComponent) {
                activeItem.$initParent = me;
                item = me.factoryItem(activeItem);
            }
            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.fx.layout.Card(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.fireEvent('deactivate', oldActiveItem, this, newActiveItem);
        }
        if (newActiveItem) {
            newActiveItem.fireEvent('activate', newActiveItem, this, oldActiveItem);
        }
    },
    show: function() {
        this.callParent(arguments);
        var modal = this.getModal();
        if (modal) {
            modal.setHidden(false);
        }
        return this;
    },
    hide: function() {
        this.callParent(arguments);
        var modal = this.getModal();
        if (modal) {
            modal.setHidden(true);
        }
        return this;
    },
    updateHidden: function(hidden) {
        var modal = this.getModal();
        if (modal && (modal.getHidden() !== hidden)) {
            modal.setHidden(hidden);
        }
        this.callParent(arguments);
    },
    
    setRendered: function(rendered) {
        if (this.callParent(arguments)) {
            var items = this.items.items,
                i, ln;
            for (i = 0 , ln = items.length; i < ln; i++) {
                items[i].setRendered(rendered);
            }
            return true;
        }
        return false;
    },
    
    getRefItems: function(deep) {
        var items = this.getItems().items.slice(),
            ln = items.length,
            i, item;
        if (deep) {
            for (i = 0; i < ln; i++) {
                item = items[i];
                if (item.getRefItems) {
                    items = items.concat(item.getRefItems(true));
                }
            }
        }
        return items;
    },
    
    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;
    },
    destroy: function() {
        var me = this,
            modal = me.getModal();
        if (modal) {
            modal.destroy();
        }
        me.removeAll(true, true);
        me.callParent();
        Ext.destroy(me.items);
        me.items = null;
    },
    privates: {
        applyReference: function(reference) {
            
            return this.setupReference(reference);
        },
        
        getFirstReferences: function() {
            var me = this;
            delete me.getReferences;
            me.getItems();
            
            return me.getReferences.apply(me, arguments);
        }
    }
}, function() {
    this.prototype.defaultItemClass = this;
});


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.viewport.Default', {
    extend: Ext.Container,
    xtype: 'viewport',
    PORTRAIT: 'portrait',
    LANDSCAPE: 'landscape',
    
    
    
    config: {
        
        autoMaximize: false,
        
        autoBlurInput: true,
        
        preventPanning: true,
        
        preventZooming: false,
        
        autoRender: true,
        
        layout: 'card',
        
        width: '100%',
        
        height: '100%',
        useBodyElement: true,
        
        menus: {}
    },
    
    isReady: false,
    isViewport: true,
    isMaximizing: false,
    id: 'ext-viewport',
    isInputRegex: /^(input|textarea|select|a)$/i,
    isInteractiveWebComponentRegEx: /^(audio|video)$/i,
    focusedElement: null,
    
    fullscreenItemCls: Ext.baseCSSPrefix + 'fullscreen',
    constructor: function(config) {
        var me = this,
            bind = Ext.Function.bind,
            Component = Ext.Component,
            DomScroller = Ext.scroll.DomScroller;
        
        
        
        if (DomScroller.document) {
            DomScroller.document = DomScroller.document.destroy();
        }
        me.doPreventPanning = bind(me.doPreventPanning, me);
        me.doPreventZooming = bind(me.doPreventZooming, me);
        me.doBlurInput = bind(me.doBlurInput, me);
        me.maximizeOnEvents = [
            'ready',
            'orientationchange'
        ];
        
        window.devicePixelRatio = window.devicePixelRatio || 1;
        me.callParent([
            config
        ]);
        me.orientation = me.determineOrientation();
        me.windowWidth = me.getWindowWidth();
        me.windowHeight = me.getWindowHeight();
        me.windowOuterHeight = me.getWindowOuterHeight();
        me.stretchHeights = me.stretchHeights || {};
        
        if (!Ext.os.is.Android || Ext.browser.is.ChromeMobile) {
            if (me.supportsOrientation()) {
                me.addWindowListener('orientationchange', bind(me.onOrientationChange, me));
            } else {
                me.addWindowListener('resize', bind(me.onResize, me));
            }
        }
        document.addEventListener('focus', bind(me.onElementFocus, me), true);
        document.addEventListener('blur', bind(me.onElementBlur, me), true);
        Ext.onDocumentReady(me.onDomReady, me);
        if (!Component.on) {
            Ext.util.Observable.observe(Component);
        }
        Component.on('fullscreen', 'onItemFullscreenChange', me);
        return me;
    },
    initialize: function() {
        var me = this;
        me.addMeta('apple-mobile-web-app-capable', 'yes');
        me.addMeta('apple-touch-fullscreen', 'yes');
        me.callParent();
    },
    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();
        }
        if (Ext.browser.name === 'ChromeiOS') {
            this.setHeight('-webkit-calc(100% - ' + ((window.outerHeight - window.innerHeight) / 2) + 'px)');
        }
    },
    onElementFocus: function(e) {
        this.focusedElement = e.target;
    },
    onElementBlur: function() {
        this.focusedElement = null;
    },
    render: function() {
        if (!this.rendered) {
            var body = Ext.getBody(),
                clsPrefix = Ext.baseCSSPrefix,
                classList = [],
                osEnv = Ext.os,
                osName = osEnv.name.toLowerCase(),
                browserName = Ext.browser.name.toLowerCase(),
                osMajorVersion = osEnv.version.getMajor(),
                orientation = this.getOrientation(),
                theme;
            this.renderTo(body);
            classList.push(clsPrefix + osEnv.deviceType.toLowerCase());
            if (osEnv.is.iPad) {
                classList.push(clsPrefix + 'ipad');
            }
            classList.push(clsPrefix + osName);
            classList.push(clsPrefix + browserName);
            if (osMajorVersion) {
                classList.push(clsPrefix + osName + '-' + osMajorVersion);
            }
            if (osEnv.is.BlackBerry) {
                classList.push(clsPrefix + 'bb');
                if (Ext.browser.userAgent.match(/Kbd/gi)) {
                    classList.push(clsPrefix + 'bb-keyboard');
                }
            }
            if (Ext.browser.is.WebKit) {
                classList.push(clsPrefix + 'webkit');
            }
            if (Ext.browser.is.Standalone) {
                classList.push(clsPrefix + 'standalone');
            }
            if (Ext.browser.is.AndroidStock) {
                classList.push(clsPrefix + 'android-stock');
            }
            if (Ext.browser.is.GoogleGlass) {
                classList.push(clsPrefix + 'google-glass');
            }
            classList.push(clsPrefix + orientation);
            body.addCls(classList);
            theme = Ext.theme;
            if (theme && theme.getDocCls) {
                
                Ext.fly(document.documentElement).addCls(theme.getDocCls());
            }
        }
    },
    updateAutoBlurInput: function(autoBlurInput) {
        var touchstart = Ext.feature.has.TouchEvents ? 'touchstart' : 'mousedown';
        this.toggleWindowListener(autoBlurInput, touchstart, this.doBlurInput, false);
    },
    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();
    },
    doBlurInput: function(e) {
        var target = e.target,
            focusedElement = this.focusedElement;
        
        
        if (focusedElement && focusedElement.blur && focusedElement.nodeName.toUpperCase() != 'BODY' && !this.isInputRegex.test(target.tagName)) {
            delete this.focusedElement;
            focusedElement.blur();
        }
    },
    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;
    },
    onResize: function() {
        var me = this,
            oldWidth = me.windowWidth,
            oldHeight = me.windowHeight,
            width = me.getWindowWidth(),
            height = me.getWindowHeight(),
            currentOrientation = me.getOrientation(),
            newOrientation = me.determineOrientation();
        
        
        if ((oldWidth !== width && oldHeight !== height) && currentOrientation !== newOrientation) {
            me.fireOrientationChangeEvent(newOrientation, currentOrientation);
        }
    },
    onOrientationChange: function() {
        var currentOrientation = this.getOrientation(),
            newOrientation = this.determineOrientation();
        if (newOrientation !== currentOrientation) {
            this.fireOrientationChangeEvent(newOrientation, currentOrientation);
        }
    },
    fireOrientationChangeEvent: function(newOrientation, oldOrientation) {
        var me = this,
            clsPrefix = Ext.baseCSSPrefix;
        Ext.getBody().replaceCls(clsPrefix + oldOrientation, clsPrefix + newOrientation);
        me.orientation = newOrientation;
        me.updateSize();
        me.fireEvent('orientationchange', me, newOrientation, me.windowWidth, me.windowHeight);
    },
    updateSize: function(width, height) {
        var me = this;
        me.windowWidth = width !== undefined ? width : me.getWindowWidth();
        me.windowHeight = height !== undefined ? height : me.getWindowHeight();
        return me;
    },
    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;
    },
    
    getOrientation: function() {
        return this.orientation;
    },
    getSize: function() {
        return {
            width: this.windowWidth,
            height: this.windowHeight
        };
    },
    determineOrientation: function() {
        var me = this,
            portrait = me.PORTRAIT,
            landscape = me.LANDSCAPE;
        if (!Ext.os.is.Android && me.supportsOrientation()) {
            if (me.getWindowOrientation() % 180 === 0) {
                return portrait;
            }
            return landscape;
        } else {
            if (me.getWindowHeight() >= me.getWindowWidth()) {
                return portrait;
            }
            return landscape;
        }
    },
    onItemFullscreenChange: function(item) {
        item.addCls(this.fullscreenItemCls);
        this.add(item);
    },
    
    setMenu: function(menu, config) {
        config = config || {};
        var me = this,
            side = config.side,
            menus;
        
        if (Ext.os.is.iOS && !me.hasiOSOrientationFix) {
            me.hasiOSOrientationFix = true;
            me.on('orientationchange', function() {
                window.scrollTo(0, 0);
            }, me);
        }
        
        if (!menu) {
            Ext.Logger.error("You must specify a side to dock the menu.");
        }
        if (!side) {
            Ext.Logger.error("You must specify a side to dock the menu.");
        }
        if ([
            'left',
            'right',
            'top',
            'bottom'
        ].indexOf(side) == -1) {
            Ext.Logger.error("You must specify a valid side (left, right, top or botom) to dock the menu.");
        }
        
        menus = me.getMenus();
        if (!menus) {
            menus = {};
        }
        if (!me.addedSwipeListener) {
            me.attachSwipeListeners();
            me.addedSwipeListener = true;
        }
        
        
        
        if (!menu.isComponent) {
            if (!menu.xclass && !menu.xtype) {
                menu = me.getMenuCfg(menu, side);
            }
            menu = Ext.create(menu);
        }
        menus[side] = menu;
        menu.$reveal = Boolean(config.reveal);
        menu.$cover = config.cover !== false && !menu.$reveal;
        menu.$side = side;
        me.fixMenuSize(menu, side);
        if (side == 'left') {
            menu.setLeft(0);
            menu.setRight(null);
            menu.setTop(0);
            menu.setBottom(0);
        } else if (side == 'right') {
            menu.setLeft(null);
            menu.setRight(0);
            menu.setTop(0);
            menu.setBottom(0);
        } else if (side == 'top') {
            menu.setLeft(0);
            menu.setRight(0);
            menu.setTop(0);
            menu.setBottom(null);
        } else if (side == 'bottom') {
            menu.setLeft(0);
            menu.setRight(0);
            menu.setTop(null);
            menu.setBottom(0);
        }
        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
        });
    },
    getMenuCfg: function(menu, side) {
        return Ext.apply({
            xtype: 'menu'
        }, menu);
    },
    
    removeMenu: function(side) {
        var menus = this.getMenus() || {},
            menu = menus[side];
        if (menu) {
            this.hideMenu(side);
        }
        delete menus[side];
        this.setMenus(menus);
    },
    
    fixMenuSize: function(menu, side) {
        if (side == 'top' || side == 'bottom') {
            menu.setWidth('100%');
        } else if (side == 'left' || side == 'right') {
            menu.setHeight('100%');
        }
    },
    
    showMenu: function(side) {
        var me = this,
            menus = me.getMenus(),
            menu = menus[side],
            before, after, viewportBefore, viewportAfter, size;
        if (!menu || menu.isAnimating) {
            return;
        }
        me.hideOtherMenus(side);
        before = {
            translateX: 0,
            translateY: 0
        };
        after = {
            translateX: 0,
            translateY: 0
        };
        viewportBefore = {
            translateX: 0,
            translateY: 0
        };
        viewportAfter = {
            translateX: 0,
            translateY: 0
        };
        if (menu.$reveal) {
            Ext.getBody().insertFirst(menu.element);
        } else {
            Ext.Viewport.add(menu);
        }
        menu.show();
        menu.addCls('x-' + side);
        size = (side == 'left' || side == 'right') ? menu.element.getWidth() : menu.element.getHeight();
        if (side == 'left') {
            before.translateX = -size;
            viewportAfter.translateX = size;
        } else if (side == 'right') {
            before.translateX = size;
            viewportAfter.translateX = -size;
        } else if (side == 'top') {
            before.translateY = -size;
            viewportAfter.translateY = size;
        } else if (side == 'bottom') {
            before.translateY = size;
            viewportAfter.translateY = -size;
        }
        if (menu.$reveal) {
            if (Ext.browser.getPreferredTranslationMethod() != 'scrollposition') {
                menu.translate(0, 0);
            }
        } else {
            menu.translate(before.translateX, before.translateY);
        }
        if (menu.$cover) {
            menu.getTranslatable().on('animationend', function() {
                menu.isAnimating = false;
            }, me, {
                single: true
            });
            menu.translate(after.translateX, after.translateY, {
                preserveEndState: true,
                duration: 200
            });
        } else {
            me.translate(viewportBefore.translateX, viewportBefore.translateY);
            me.getTranslatable().on('animationend', function() {
                menu.isAnimating = false;
            }, me, {
                single: true
            });
            me.translate(viewportAfter.translateX, viewportAfter.translateY, {
                preserveEndState: true,
                duration: 200
            });
        }
        
        menu.isAnimating = true;
    },
    
    hideMenu: function(side, animate) {
        var me = this,
            menus = this.getMenus(),
            menu = menus[side],
            after, viewportAfter, size;
        animate = animate !== false;
        if (!menu || (menu.isHidden() || menu.isAnimating)) {
            return;
        }
        after = {
            translateX: 0,
            translateY: 0
        };
        viewportAfter = {
            translateX: 0,
            translateY: 0
        };
        size = (side == 'left' || side == 'right') ? menu.element.getWidth() : menu.element.getHeight();
        if (side == 'left') {
            after.translateX = -size;
        } else if (side == 'right') {
            after.translateX = size;
        } else if (side == 'top') {
            after.translateY = -size;
        } else if (side == 'bottom') {
            after.translateY = size;
        }
        if (menu.$cover) {
            if (animate) {
                menu.getTranslatable().on('animationend', function() {
                    menu.isAnimating = false;
                    menu.hide();
                }, me, {
                    single: true
                });
                menu.translate(after.translateX, after.translateY, {
                    preserveEndState: true,
                    duration: 200
                });
            } else {
                menu.translate(after.translateX, after.translateY);
                menu.hide();
            }
        } else {
            if (animate) {
                me.getTranslatable().on('animationend', function() {
                    menu.isAnimating = false;
                    menu.hide();
                }, me, {
                    single: true
                });
                me.translate(viewportAfter.translateX, viewportAfter.translateY, {
                    preserveEndState: true,
                    duration: 200
                });
            } else {
                me.translate(viewportAfter.translateX, viewportAfter.translateY);
                menu.hide();
            }
        }
    },
    
    hideAllMenus: function(animation) {
        var menus = this.getMenus(),
            side;
        for (side in menus) {
            this.hideMenu(side, animation);
        }
    },
    
    hideOtherMenus: function(side, animation) {
        var menus = this.getMenus(),
            menu;
        for (menu in menus) {
            if (side !== menu) {
                this.hideMenu(menu, animation);
            }
        }
    },
    
    toggleMenu: function(side) {
        var menus = this.getMenus(),
            menu;
        if (menus[side]) {
            menu = menus[side];
            if (menu.isHidden()) {
                this.showMenu(side);
            } else {
                this.hideMenu(side);
            }
        }
    },
    
    sideForDirection: function(direction) {
        if (direction === 'left') {
            return 'right';
        } else if (direction === 'right') {
            return 'left';
        } else if (direction == 'up') {
            return 'bottom';
        } else if (direction == 'down') {
            return 'top';
        }
    },
    
    sideForSwipeDirection: function(direction) {
        if (direction == 'up') {
            return 'top';
        } else if (direction == 'down') {
            return 'bottom';
        }
        return direction;
    },
    
    onTap: function(e) {},
    
    
    onSwipeStart: function(e) {
        var side = this.sideForSwipeDirection(e.direction);
        this.hideMenu(side);
    },
    
    onEdgeSwipeStart: function(e) {
        var me = this,
            side = me.sideForDirection(e.direction),
            menus = me.getMenus(),
            menu = menus[side],
            menuSide, checkMenu, size, after, viewportAfter, transformStyleName, setTransform;
        if (!menu || !menu.isHidden()) {
            return;
        }
        for (menuSide in menus) {
            checkMenu = menus[menuSide];
            if (checkMenu.isHidden() !== false) {
                return;
            }
        }
        me.$swiping = true;
        me.hideAllMenus(false);
        
        if (menu.$reveal) {
            Ext.getBody().insertFirst(menu.element);
        } else {
            Ext.Viewport.add(menu);
        }
        menu.show();
        size = (side == 'left' || side == 'right') ? menu.element.getWidth() : menu.element.getHeight();
        after = {
            translateX: 0,
            translateY: 0
        };
        viewportAfter = {
            translateX: 0,
            translateY: 0
        };
        if (side == 'left') {
            after.translateX = -size;
        } else if (side == 'right') {
            after.translateX = size;
        } else if (side == 'top') {
            after.translateY = -size;
        } else if (side == 'bottom') {
            after.translateY = size;
        }
        transformStyleName = 'webkitTransform' in document.createElement('div').style ? 'webkitTransform' : 'transform';
        setTransform = menu.element.dom.style[transformStyleName];
        if (setTransform) {
            menu.element.dom.style[transformStyleName] = '';
        }
        if (menu.$reveal) {
            if (Ext.browser.getPreferredTranslationMethod() != 'scrollposition') {
                menu.translate(0, 0);
            }
        } else {
            menu.translate(after.translateX, after.translateY);
        }
        if (!menu.$cover) {
            if (setTransform) {
                me.innerElement.dom.style[transformStyleName] = '';
            }
            me.translate(viewportAfter.translateX, viewportAfter.translateY);
        }
    },
    
    onEdgeSwipe: function(e) {
        var me = this,
            side = me.sideForDirection(e.direction),
            menu = me.getMenus()[side],
            size, after, viewportAfter, movement, viewportMovement;
        if (!menu || !me.$swiping) {
            return;
        }
        size = (side == 'left' || side == 'right') ? menu.element.getWidth() : menu.element.getHeight();
        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;
        }
        if (menu.$cover) {
            menu.translate(after.translateX, after.translateY);
        } else {
            me.translate(viewportAfter.translateX, viewportAfter.translateY);
        }
    },
    
    onEdgeSwipeEnd: function(e) {
        var me = this,
            side = me.sideForDirection(e.direction),
            menu = me.getMenus()[side],
            shouldRevert = false,
            size, velocity, movement, viewportMovement, after, viewportAfter;
        if (!menu) {
            return;
        }
        size = (side == 'left' || side == 'right') ? menu.element.getWidth() : menu.element.getHeight();
        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;
        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;
        }
        
        if (menu.$cover) {
            menu.getTranslatable().on('animationend', function() {
                if (shouldRevert) {
                    menu.hide();
                }
            }, me, {
                single: true
            });
            menu.translate(after.translateX, after.translateY, {
                preserveEndState: true,
                duration: 200
            });
        } else {
            me.getTranslatable().on('animationend', function() {
                if (shouldRevert) {
                    menu.hide();
                }
            }, me, {
                single: true
            });
            me.translate(viewportAfter.translateX, viewportAfter.translateY, {
                preserveEndState: true,
                duration: 200
            });
        }
        me.$swiping = false;
    },
    privates: {
        addMeta: function(name, content) {
            var meta = document.createElement('meta');
            meta.setAttribute('name', name);
            meta.setAttribute('content', content);
            Ext.getHead().append(meta);
        },
        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
            ]);
        },
        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);
        if (this.getAutoMaximize() && !this.isFullscreen()) {
            this.addWindowListener('touchstart', Ext.Function.bind(this.onTouchStart, this));
        }
    },
    maximize: function() {
        if (this.isFullscreen()) {
            return this.callParent();
        }
        var stretchHeights = this.stretchHeights,
            orientation = this.orientation,
            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.orientation === this.PORTRAIT ? 'height' : 'width'];
    },
    onElementFocus: function() {
        if (this.getAutoMaximize() && !this.isFullscreen()) {
            clearTimeout(this.scrollToTopTimer);
        }
        this.callParent(arguments);
    },
    onElementBlur: function() {
        if (this.getAutoMaximize() && !this.isFullscreen()) {
            this.scrollToTopTimer = Ext.defer(this.scrollToTop, 500);
        }
        this.callParent(arguments);
    },
    onTouchStart: function() {
        if (this.focusedElement === null) {
            this.scrollToTop();
        }
    },
    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')) {
        
        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.orientation];
                },
                getWindowWidth: function() {
                    return this.stretchWidths[this.orientation];
                },
                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: {
            translationMethod: 'csstransform'
        }
    },
    constructor: function() {
        this.callParent(arguments);
        this.on({
            orientationchange: 'hideKeyboardIfNeeded',
            scope: this,
            
            priority: 1001
        });
        
        
        var me = this;
        Ext.onReady(function() {
            Ext.getBody().on('resize', me.onResize, me);
        });
    },
    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;
    },
    determineOrientation: function() {
        return (this.getWindowHeight() >= this.getWindowWidth()) ? this.PORTRAIT : this.LANDSCAPE;
    },
    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 = Ext.Function.bind(this.isHeightMaximized, this, [
                height
            ]);
        this.scrollToTop();
        this.waitUntil(isHeightMaximized, this.fireMaximizeEvent, this.fireMaximizeEvent);
    },
    isHeightMaximized: function(height) {
        this.scrollToTop();
        return this.getWindowHeight() === height;
    },
    supportsOrientation: function() {
        return false;
    },
    onResize: function() {
        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);
    },
    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 = Ext.Function.bind(this.watchDogTick, 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', Ext.Function.bind(this.onWindowResize, 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', {
    requires: [],
    alternateClassName: 'Ext.viewport.WP',
    extend: Ext.viewport.Default,
    
    
    
    
    
    config: {
        translatable: {
            translationMethod: '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;
    },
    onResize: function() {
        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;
        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;
        }
        Ext.Viewport = Ext.create('Ext.viewport.' + viewportName, config);
    }
});



Ext.define('Ext.overrides.app.Application', {
    override: 'Ext.app.Application',
    initMainView: function() {
        var me = this,
            viewport, mainView;
        me.viewport = viewport = Ext.Viewport;
        me.callParent();
        mainView = me.getMainView();
        
        viewport.onAppLaunch();
        if (mainView) {
            viewport.add(mainView);
        }
    }
});


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(arguments);
        this.controller = controller;
        this.monitoredClasses = [
            Ext.Component
        ];
    },
    match: function(target, selector, controller) {
        var out = false;
        if (selector === '#') {
            out = controller === target.getController();
        } else {
            out = target.is(selector);
        }
        return out;
    },
    destroy: function() {
        this.controller = null;
        this.callParent();
    }
});


Ext.define('Ext.app.ViewController', {
    extend: Ext.app.BaseController,
    mixins: [
        Ext.mixin.Factoryable
    ],
    isViewController: true,
    factoryConfig: {
        
        type: 'controller'
    },
    config: {
        closeViewAction: 'destroy'
    },
    view: null,
    constructor: function() {
        this.compDomain = new Ext.app.domain.View(this);
        this.callParent(arguments);
    },
    
    beforeInit: Ext.emptyFn,
    
    init: Ext.emptyFn,
    
    initViewModel: Ext.emptyFn,
    
    destroy: function() {
        var me = this,
            domain = me.compDomain;
        if (domain) {
            domain.unlisten(me);
            domain.destroy();
        }
        me.compDomain = me.view = null;
        me.callParent();
    },
    
    closeView: function() {
        var view = this.getView(),
            action;
        if (view) {
            action = this.getCloseViewAction();
            view[action]();
        }
    },
    control: function(selectors, listeners) {
        var obj = selectors;
        if (Ext.isString(selectors)) {
            obj = {};
            obj[selectors] = listeners;
        }
        this.compDomain.listen(obj, this);
    },
    listen: function(to, controller) {
        var component = to.component;
        if (component) {
            to = Ext.apply({}, to);
            delete to.component;
            this.control(component);
        }
        this.callParent([
            to,
            controller
        ]);
    },
    
    getReferences: function() {
        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, firstArg) {
        var view = this.view,
            result = false,
            args = arguments;
        if (view) {
            if (view !== firstArg) {
                args = Ext.Array.slice(args);
                args.splice(1, 0, view);
            }
            result = view.fireEvent.apply(view, args);
        }
        return result;
    },
    
    privates: {
        view: null,
        ensureId: function() {
            var id = this.getId();
            if (!id) {
                this.setId(Ext.id(null, 'controller-'));
            }
        },
        
        attachReference: function(component) {
            var view = this.view;
            if (view) {
                view.attachReference(component);
            }
        },
        
        clearReference: function(ref) {
            var view = this.view;
            if (view) {
                view.clearReference(ref);
            }
        },
        
        clearReferences: function() {
            var view = this.view;
            if (view) {
                view.clearReferences();
            }
        },
        
        setView: function(view) {
            this.view = view;
            if (!this.beforeInit.$nullFn) {
                this.beforeInit(view);
            }
        }
    }
});


Ext.define('Ext.util.Bag', {
    isBag: true,
    constructor: function() {
        
        this.items = [];
        
        this.map = {};
    },
    
    generation: 0,
    
    length: 0,
    add: function(item) {
        var me = this,
            id = me.getKey(item),
            map = me.map,
            items = me.items,
            idx = map[id],
            old;
        if (idx === undefined) {
            items.push(item);
            map[id] = me.length++;
            old = item;
        } else {
            old = items[idx];
            items[idx] = item;
        }
        ++me.generation;
        return old;
    },
    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();
    },
    getAt: function(index) {
        var out = null;
        if (index < this.length) {
            out = this.items[index];
        }
        return out;
    },
    getByKey: function(key) {
        var map = this.map,
            ret = null;
        if (key in map) {
            ret = this.items[map[key]];
        }
        return ret;
    },
    getCount: function() {
        return this.length;
    },
    getKey: function(item) {
        return item.id || item.getId();
    },
    remove: function(item) {
        var me = this,
            map = me.map,
            items = me.items,
            old = null,
            idx, id, last;
        if (me.length) {
            idx = map[id = me.getKey(item)];
            if (idx !== undefined) {
                delete map[id];
                old = items[idx];
                last = items.pop();
                if (idx < --me.length) {
                    items[idx] = last;
                    map[me.getKey(last)] = idx;
                }
                ++me.generation;
            }
        }
        return old;
    },
    removeByKey: function(key) {
        var item = this.getByKey(key);
        if (item) {
            this.remove(item);
        }
        return item || null;
    },
    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) {
            Ext.raise('Notify cannot be called recursively');
        }
        
        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.Function.defer(me.onTick, me.getTickDelay(), me);
        }
    },
    
    unscheduleItem: function(item) {
        if (this.scheduledCount) {
            --this.scheduledCount;
        }
    },
    
    
    
    
    
    adjustBusy: function(adjustment) {
        var me = this,
            busyCounter = me.busyCounter + adjustment;
        me.busyCounter = busyCounter;
        if (busyCounter) {
            
            
            if (!me.lastBusyCounter) {
                me.lastBusyCounter = busyCounter;
                me.fireEvent('busy', me);
            }
        } else if (me.lastBusyCounter && !me.timer) {
            
            
            me.scheduleTick();
        }
    },
    
    isBusy: function() {
        return !this.isIdle();
    },
    
    isIdle: function() {
        return !(this.busyCounter + this.lastBusyCounter);
    },
    
    debugHooks: {
        $enabled: false,
        
        onCycleLimitExceeded: function() {
            Ext.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) {
                    
                    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, proxy;
        if (map) {
            if (!batch) {
                batch = new Ext.data.Batch();
            }
            for (name in map) {
                bucket = map[name];
                entity = bucket.entity;
                
                proxy = entity.getProxy();
                delete bucket.entity;
                
                for (operation in bucket) {
                    operation = proxy.createOperation(operation, {
                        records: bucket[operation]
                    });
                    operation.entityType = entity;
                    batch.add(operation);
                }
            }
        }
        if (batch && sort !== false) {
            batch.sort();
        }
        return batch;
    },
    onDirtyRecord: function(record) {
        var me = this,
            operation = record.phantom ? 'create' : (record.dropped ? 'destroy' : 'update'),
            name = record.$className,
            map = (me.map || (me.map = {})),
            bucket = (map[name] || (map[name] = {
                entity: record.self
            }));
        
        
        
        
        
        
        bucket = bucket[operation] || (bucket[operation] = []);
        bucket.push(record);
    }
});


Ext.define('Ext.data.Session', {
    isSession: true,
    config: {
        
        schema: 'default',
        
        parent: null,
        
        autoDestroy: true,
        crudProperties: {
            create: 'C',
            read: 'R',
            update: 'U',
            drop: 'D'
        }
    },
    destroyed: false,
    crudOperations: [
        {
            type: 'R',
            entityMethod: 'readEntities'
        },
        {
            type: 'C',
            entityMethod: 'createEntities'
        },
        {
            type: 'U',
            entityMethod: 'updateEntities'
        },
        {
            type: 'D',
            entityMethod: 'dropEntities'
        }
    ],
    crudKeys: {
        C: 1,
        R: 1,
        U: 1,
        D: 1
    },
    constructor: function(config) {
        var me = this;
        
        me.data = {};
        
        me.matrices = {};
        me.identifierCache = {};
        
        me.recordCreator = me.recordCreator.bind(me);
        me.initConfig(config);
    },
    destroy: function() {
        var me = this,
            matrices = me.matrices,
            data = me.data,
            entityName, entities, record, id;
        for (id in matrices) {
            matrices[id].destroy();
        }
        for (entityName in data) {
            entities = data[entityName];
            for (id in entities) {
                record = entities[id].record;
                if (record) {
                    
                    
                    record.$source = record.session = null;
                }
            }
        }
        me.recordCreator = me.matrices = me.data = null;
        me.setSchema(null);
        me.callParent();
    },
    
    adopt: function(record) {
        var me = this,
            associations = record.associations,
            roleName;
        
        me.checkModelType(record.self);
        if (record.session && record.session !== me) {
            Ext.raise('Record already belongs to an existing session');
        }
        
        if (record.session !== me) {
            record.session = me;
            me.add(record);
            if (associations) {
                for (roleName in associations) {
                    associations[roleName].adoptAssociated(record, me);
                }
            }
        }
    },
    
    commit: function() {
        var data = this.data,
            matrices = this.matrices,
            entityName, entities, id, record;
        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();
        }
    },
    
    createRecord: function(type, data) {
        
        this.checkModelType(type);
        
        var Model = type.$isClass ? type : this.getSchema().getEntity(type),
            parent = this.getParent(),
            id;
        
        if (data && parent) {
            id = Model.getIdFromData(data);
            if (parent.peekRecord(Model, id)) {
                Ext.raise('A parent session already contains an entry for ' + Model.entityName + ': ' + id);
            }
        }
        
        return new Model(data, this);
    },
    
    getChanges: function() {
        var visitor = new Ext.data.session.ChangesVisitor(this);
        this.visitData(visitor);
        return visitor.result;
    },
    
    getChangesForParent: function() {
        var visitor = new Ext.data.session.ChildChangesVisitor(this);
        this.visitData(visitor);
        return visitor.result;
    },
    
    getRecord: function(type, id, autoLoad) {
        var me = this,
            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() {
        
        if (!this.getParent()) {
            Ext.raise('Cannot commit session, no parent exists');
        }
        
        var visitor = new Ext.data.session.ChildChangesVisitor(this);
        this.visitData(visitor);
        this.getParent().update(visitor.result);
    },
    
    spawn: function() {
        return new this.self({
            schema: this.getSchema(),
            parent: this
        });
    },
    
    update: function(data) {
        var me = this,
            schema = me.getSchema(),
            crudOperations = me.crudOperations,
            len = crudOperations.length,
            crudKeys = me.crudKeys,
            entityName, entityType, entityInfo, i, operation, item, associations, key, role, associationData;
        
        me.getSchema().processKeyChecks(true);
        
        for (entityName in data) {
            entityType = schema.getEntity(entityName);
            
            if (!entityType) {
                Ext.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);
            }
        }
    },
    
    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);
            }
            
            entry.record = record;
            me.registerReferences(record);
            associations = record.associations;
            for (roleName in associations) {
                associations[roleName].checkMembership(me, record);
            }
        },
        
        afterErase: function(record) {
            this.evict(record);
        },
        
        applySchema: function(schema) {
            return Ext.data.schema.Schema.get(schema);
        },
        
        
        checkModelType: function(name) {
            if (name.$isClass) {
                name = name.entityName;
            }
            if (!name) {
                Ext.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 len = items.length,
                i, data, rec, id;
            for (i = 0; i < len; ++i) {
                data = items[i];
                id = entityType.getIdFromData(data);
                rec = this.peekRecord(entityType, id);
                if (!rec) {
                    rec = this.createRecord(entityType, data);
                } else {
                    this.onInvalidEntityCreate(entityType, id);
                }
                
                
                rec.phantom = true;
            }
        },
        
        dropEntities: function(entityType, ids) {
            var len = ids.length,
                i, rec, id, 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 entityName = record.entityName,
                entities = this.data[entityName],
                id = record.id,
                entry;
            if (entities) {
                delete entities[id];
            }
        },
        
        getEntityList: function(entityType, ids) {
            var len = ids.length,
                i, id, rec, invalid;
            for (i = 0; i < len; ++i) {
                id = ids[i];
                rec = this.peekRecord(entityType, id);
                if (rec) {
                    ids[i] = rec;
                } else {
                    invalid = true;
                    ids[i] = null;
                    this.onInvalidAssociationEntity(entityType, id);
                }
            }
            if (invalid) {
                ids = Ext.Array.clean(ids);
            }
            return ids;
        },
        
        getEntry: function(type, id) {
            if (type.isModel) {
                id = type.getId();
                type = type.self;
            }
            var entityType = type.$isClass ? type : this.getSchema().getEntity(type),
                entityName = entityType.entityName,
                data = this.data,
                entry;
            entry = data[entityName] || (data[entityName] = {});
            entry = entry[id] || (entry[id] = {});
            return entry;
        },
        getRefs: function(record, role, includeParent) {
            var entry = this.getEntry(record),
                refs = entry && entry.refs && entry.refs[role.role],
                parent = includeParent && this.getParent(),
                parentRefs, id, rec;
            if (parent) {
                parentRefs = parent.getRefs(record, role);
                if (parentRefs) {
                    for (id in parentRefs) {
                        rec = parentRefs[id];
                        if ((!refs || !refs[id])) {
                            
                            
                            this.getRecord(rec.self, rec.id);
                        }
                    }
                    
                    refs = entry && entry.refs && entry.refs[role.role];
                }
            }
            return refs || null;
        },
        getIdentifier: function(entityType) {
            var parent = this.getParent(),
                cache, identifier, key, ret;
            if (parent) {
                ret = parent.getIdentifier(entityType);
            } else {
                cache = this.identifierCache;
                identifier = entityType.identifier;
                key = identifier.id || entityType.entityName;
                ret = cache[key];
                if (!ret) {
                    if (identifier.clone) {
                        ret = identifier.clone({
                            cache: cache
                        });
                    } else {
                        ret = identifier;
                    }
                    cache[key] = ret;
                }
            }
            return ret;
        },
        getMatrix: function(matrix, preventCreate) {
            var name = matrix.isManyToMany ? matrix.name : matrix,
                matrices = this.matrices,
                ret;
            ret = matrices[name];
            if (!ret && !preventCreate) {
                ret = matrices[name] = new Ext.data.matrix.Matrix(this, matrix);
            }
            return ret || null;
        },
        getMatrixSlice: function(role, id) {
            var matrix = this.getMatrix(role.association),
                side = matrix[role.side];
            return side.get(id);
        },
        
        getModelIdentifier: function(entityType, id) {
            return id + '@' + entityType.entityName;
        },
        onIdChanged: function(record, oldId, newId) {
            var me = this,
                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 len = items.length,
                i, data, rec, id;
            for (i = 0; i < len; ++i) {
                data = items[i];
                id = entityType.getIdFromData(data);
                rec = this.peekRecord(entityType, id);
                if (!rec) {
                    rec = this.createRecord(entityType, data);
                } else {
                    this.onInvalidEntityRead(entityType, id);
                }
                
                
                rec.phantom = false;
            }
        },
        recordCreator: function(data, Model) {
            var me = this,
                id = Model.getIdFromData(data),
                record = me.peekRecord(Model, id, true);
            
            if (!record) {
                
                
                
                
                record = new Model(data, me);
            } else {
                
                
                
                
                
                record = me.getRecord(Model, id);
            }
            return record;
        },
        registerReferences: function(record, oldId) {
            var entityName = record.entityName,
                id = record.id,
                recordData = record.data,
                remove = oldId || oldId === 0,
                entry, i, fk, len, reference, references, refs, roleName;
            
            len = (references = record.references).length;
            for (i = 0; i < len; ++i) {
                reference = references[i];
                
                fk = recordData[reference.name];
                
                if (fk || fk === 0) {
                    reference = reference.reference;
                    
                    entityName = reference.type;
                    roleName = reference.inverse.role;
                    
                    entry = this.getEntry(reference.cls, fk);
                    refs = entry.refs || (entry.refs = {});
                    refs = refs[roleName] || (refs[roleName] = {});
                    refs[id] = record;
                    if (remove) {
                        delete refs[oldId];
                    }
                }
            }
        },
        
        updateEntities: function(entityType, items) {
            var len = items.length,
                i, data, rec, id, modified;
            
            if (Ext.isArray(items)) {
                for (i = 0; i < len; ++i) {
                    data = items[i];
                    id = entityType.getIdFromData(data);
                    rec = this.peekRecord(entityType, id);
                    if (rec) {
                        rec.set(data);
                    } else {
                        this.onInvalidEntityUpdate(entityType, id);
                    }
                }
            } else {
                for (id in items) {
                    data = items[id];
                    rec = this.peekRecord(entityType, id);
                    if (rec && !rec.dropped) {
                        modified = rec.set(data);
                    } else {
                        this.onInvalidEntityUpdate(entityType, id, !!rec);
                    }
                }
            }
        },
        updateReference: function(record, field, newValue, oldValue) {
            var reference = field.reference,
                entityName = reference.type,
                roleName = reference.inverse.role,
                id = record.id,
                entry, refs;
            if (oldValue || oldValue === 0) {
                
                refs = this.getEntry(entityName, oldValue).refs[roleName];
                delete refs[id];
            }
            if (newValue || newValue === 0) {
                entry = this.getEntry(entityName, newValue);
                refs = entry.refs || (entry.refs = {});
                refs = refs[roleName] || (refs[roleName] = {});
                refs[id] = record;
            }
        },
        
        visitData: function(visitor) {
            var me = this,
                data = me.data,
                matrices = me.matrices,
                all, assoc, id, id2, matrix, members, name, record, slice, slices, state;
            
            me.getSchema().processKeyChecks(true);
            for (name in data) {
                all = data[name];
                
                for (id in all) {
                    record = all[id].record;
                    if (record) {
                        if (record.phantom || record.dirty || record.dropped) {
                            if (visitor.onDirtyRecord) {
                                visitor.onDirtyRecord(record);
                            }
                        } else if (visitor.onCleanRecord) {
                            visitor.onCleanRecord(record);
                        }
                    }
                }
            }
            if (visitor.onMatrixChange) {
                for (name in matrices) {
                    matrix = matrices[name].left;
                    
                    slices = matrix.slices;
                    assoc = matrix.role.association;
                    for (id in slices) {
                        slice = slices[id];
                        members = slice.members;
                        for (id2 in members) {
                            state = (record = members[id2])[2];
                            if (state) {
                                visitor.onMatrixChange(assoc, record[0], record[1], state);
                            }
                        }
                    }
                }
            }
            return visitor;
        },
        
        
        _setNoRefs: {
            refs: false
        }
    }
});


Ext.define('Ext.util.Schedulable', {
    'abstract': true,
    isSchedulable: true,
    scheduled: false,
    constructor: function() {
        this.getScheduler().add(this);
    },
    destroy: function() {
        var me = this,
            scheduler = me.getScheduler();
        if (scheduler) {
            scheduler.remove(me);
        }
        me.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.unscheduleItem(me);
                }
                me.scheduled = false;
            }
        },
        
        
        sort: function() {}
    }
});


Ext.define('Ext.app.bind.BaseBinding', {
    extend: Ext.util.Schedulable,
    calls: 0,
    kind: 20,
    defaultOptions: {},
    lastValue: undefined,
    
    constructor: function(owner, callback, scope, options) {
        var me = this;
        me.options = options;
        me.owner = owner;
        me.scope = scope;
        me.callback = callback;
        
        if (!callback) {
            Ext.raise('Callback is required');
        }
        
        
        me.lateBound = Ext.isString(callback);
        if (options && options.deep) {
            me.deep = true;
        }
        me.callParent();
    },
    destroy: function() {
        var me = this,
            owner = me.owner;
        me.callParent();
        if (owner) {
            owner.onBindDestroy(me);
        }
        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 || previous !== value || Ext.isArray(value)) {
                ++me.calls;
                me.lastValue = value;
                if (me.lateBound) {
                    
                    
                    me.scope[me.callback](value, previous, me);
                } else {
                    me.callback.call(me.scope, value, previous, me);
                }
                if (options.single) {
                    me.destroy();
                }
            }
        }
    }
});


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




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




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


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


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


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




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




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


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




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





















Ext.define('Ext.app.ViewModel', {
    mixins: [
        Ext.mixin.Factoryable,
        Ext.mixin.Identifiable
    ],
    alias: 'viewmodel.default',
    
    isViewModel: true,
    factoryConfig: {
        name: 'viewModel'
    },
    collectTimeout: 100,
    expressionRe: /^(?:\{[!]?(?:(\d+)|([a-z_][\w\-\.]*))\})$/i,
    $configStrict: false,
    
    config: {
        
        data: true,
        
        formulas: {
            $value: null,
            merge: function(newValue, currentValue, target, mixinClass) {
                return this.mergeNew(newValue, currentValue, target, mixinClass);
            }
        },
        
        links: null,
        
        parent: null,
        
        root: true,
        
        scheduler: null,
        
        schema: 'default',
        
        session: null,
        
        
        stores: null,
        
        view: null
    },
    constructor: function(config) {
        this.hadValue = {};
        
        this.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;
        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];
                autoDestroy = store.autoDestroy;
                if (autoDestroy || (!store.$wasInstance && autoDestroy !== false)) {
                    store.destroy();
                }
                Ext.destroy(store.$binding);
            }
        }
        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.hadValue = 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,
            binding, track;
        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);
            track = true;
        } else if (me.expressionRe.test(descriptor)) {
            
            descriptor = descriptor.substring(1, descriptor.length - 1);
            binding = me.bindExpression(descriptor, callback, scope, options);
        } else {
            binding = new Ext.app.bind.TemplateBinding(descriptor, me, callback, scope, options);
            track = true;
        }
        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() {
        this.getScheduler().notify();
    },
    
    get: function(path) {
        return this.getStub(path).getValue();
    },
    
    set: function(path, value) {
        var me = this,
            obj, stub;
        
        me.getData();
        if (value === undefined && path && path.constructor === Object) {
            stub = me.getRoot();
            value = path;
        } else if (path && path.indexOf('.') < 0) {
            obj = {};
            obj[path] = value;
            value = obj;
            stub = me.getRoot();
        } else {
            stub = me.getStub(path);
        }
        stub.set(value);
    },
    
    privates: {
        registerChild: function(child) {
            var children = this.children;
            if (!children) {
                this.children = children = {};
            }
            children[child.getId()] = child;
        },
        unregisterChild: function(child) {
            var children = this.children;
            
            
            if (!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;
        },
        notFn: function(v) {
            return !v;
        },
        bindExpression: function(descriptor, callback, scope, options) {
            var ch = descriptor.charAt(0),
                not = (ch === '!'),
                path = not ? descriptor.substring(1) : descriptor,
                stub = this.getStub(path),
                binding;
            binding = stub.bind(callback, scope, options);
            if (not) {
                binding.transform = this.notFn;
            }
            return binding;
        },
        applyScheduler: function(scheduler) {
            if (scheduler && !scheduler.isInstance) {
                scheduler = new Ext.util.Scheduler(scheduler);
                scheduler.$owner = this;
            }
            return scheduler;
        },
        getScheduler: function() {
            var me = this,
                scheduler = me._scheduler,
                parent;
            if (!scheduler) {
                if (!(parent = me.getParent())) {
                    scheduler = new Ext.util.Scheduler({
                        
                        preSort: 'kind,-depth'
                    });
                    scheduler.$owner = me;
                } else {
                    scheduler = parent.getScheduler();
                }
                me.setScheduler(scheduler);
            }
            return scheduler;
        },
        
        getStub: function(bindDescr) {
            var root = this.getRoot();
            return bindDescr ? root.getChild(bindDescr) : root;
        },
        collect: function() {
            var me = this,
                parent = me.getParent(),
                task = me.collectTask;
            if (parent) {
                parent.collect();
                return;
            }
            if (!task) {
                task = me.collectTask = new Ext.util.DelayedTask(me.doCollect, me);
            }
            
            if (me.collectTimeout === 0) {
                me.doCollect();
            } else {
                task.delay(me.collectTimeout);
            }
        },
        doCollect: function() {
            var children = this.children,
                key;
            
            
            if (children) {
                for (key in children) {
                    children[key].doCollect();
                }
            }
            this.getRoot().collect();
        },
        onBindDestroy: function(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);
            }
        },
        applyParent: function(parent) {
            if (parent) {
                parent.registerChild(this);
            }
            return parent;
        },
        applyStores: function(stores) {
            var me = this,
                root = me.getRoot(),
                key, cfg, storeBind, stub, listeners, isStatic;
            me.storeInfo = {};
            me.listenerScopeFn = function() {
                return me.getView().getInheritedConfig('defaultListenerScope');
            };
            for (key in stores) {
                cfg = stores[key];
                if (cfg.isStore) {
                    cfg.$wasInstance = true;
                    me.setupStore(cfg, key);
                    
                    continue;
                } else if (Ext.isString(cfg)) {
                    cfg = {
                        source: cfg
                    };
                } else {
                    cfg = Ext.apply({}, cfg);
                }
                
                listeners = cfg.listeners;
                delete cfg.listeners;
                storeBind = me.bind(cfg, me.onStoreBind, me, {
                    trackStatics: true
                });
                if (storeBind.isStatic()) {
                    
                    
                    storeBind.destroy();
                    me.createStore(key, cfg, listeners);
                } else {
                    storeBind.$storeKey = key;
                    storeBind.$listeners = listeners;
                    stub = root.createStubChild(key);
                    stub.setStore(storeBind);
                }
            }
        },
        onStoreBind: function(cfg, oldValue, binding) {
            var info = this.storeInfo,
                key = binding.$storeKey,
                store = info[key],
                proxy;
            if (!store) {
                this.createStore(key, cfg, binding.$listeners, binding);
            } else {
                cfg = Ext.merge({}, binding.pruneStaticKeys());
                proxy = cfg.proxy;
                delete cfg.type;
                delete cfg.model;
                delete cfg.fields;
                delete cfg.proxy;
                delete cfg.listeners;
                
                
                if (proxy) {
                    delete proxy.reader;
                    delete proxy.writer;
                    store.getProxy().setConfig(proxy);
                }
                store.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;
            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;
    },
    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,
    
    
    
    
    
    
    subscribers: 0,
    constructor: function(config) {
        var me = this;
        Ext.apply(me, config);
        Ext.applyIf(me, {
            id: Ext.id(null, 'provider-')
        });
        me.mixins.observable.constructor.call(me, config);
    },
    destroy: function() {
        var me = this;
        me.disconnect(true);
        me.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) {
            if (force) {
                me.subscribers = 0;
            } else {
                me.subscribers--;
            }
            if (me.subscribers === 0) {
                me.doDisconnect();
                me.fireEventArgs('disconnect', [
                    me
                ]);
            }
        }
    },
    
    doDisconnect: Ext.emptyFn,
    inheritableStatics: {
        
        checkConfig: Ext.returnFalse
    },
    onClassExtended: function(cls, data, hooks) {
        if (data.type) {
            Ext.direct.Manager.addProviderClass(data.type, cls);
        }
    }
});


Ext.define('Ext.app.domain.Direct', {
    extend: Ext.app.EventDomain,
    singleton: true,
    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) {
        
        
        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;
    },
    destroy: function() {
        this.callParent();
        this.indexMap = {};
    }
});


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;
    },
    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;
        }
        
        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, Math.min(endIdx, me.totalCount))) {
                me.loading = false;
                data.un('pageadd', waitForReload);
                records = data.getRange(startIdx, endIdx + 1);
                me.fireEvent('load', me, records, true);
                me.fireEvent('refresh', me);
            }
        };
        bufferZone = Math.ceil((me.getLeadingBufferZone() + me.getTrailingBufferZone()) / 2);
        
        
        
        
        if (me.lastRequestStart && 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);
        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;
        return me.loadToPrefetch(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;
        
        
        
        
        requiredStart = start === 0 ? 0 : start - 1;
        requiredEnd = end === maxIndex ? end : end + 1;
        
        me.lastRequestStart = start;
        me.lastRequestEnd = end;
        
        if (me.rangeCached(requiredStart, requiredEnd)) {
            me.onRangeAvailable(options);
            result = data.getRange(start, end + 1);
        } else 
        {
            
            me.fireEvent('cachemiss', me, start, end);
            requiredStartPage = me.getPageFromRecordIndex(requiredStart);
            requiredEndPage = me.getPageFromRecordIndex(requiredEnd);
            
            pageAddHandler = function(pageMap, page, records) {
                if (page >= requiredStartPage && page <= requiredEndPage && me.rangeCached(requiredStart, requiredEnd)) {
                    
                    me.fireEvent('cachefilled', me, start, end);
                    data.un('pageadd', pageAddHandler);
                    me.onRangeAvailable(options);
                }
            };
            data.on('pageadd', pageAddHandler);
            
            
            
            me.prefetchRange(start, end);
        }
        
        me.primeCache(start, end, start < lastRequestStart ? -1 : 1);
        return result;
    },
    
    getById: function(id) {
        var result = this.data.findBy(function(record) {
                return record.getId() === id;
            });
        return result;
    },
    
    getAt: function(index) {
        var data = this.getData();
        if (data.hasRange(index, index)) {
            return data.getAt(index);
        }
    },
    
    getByInternalId: function(internalId) {
        return this.data.getByInternalId(internalId);
    },
    
    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) {
                me.grouper = new Ext.util.Grouper({
                    property: grouper,
                    direction: direction || 'ASC',
                    root: 'data'
                });
            } else if (direction === undefined) {
                oldGrouper.toggle();
            } else {
                oldGrouper.setDirection(direction);
            }
        } else {
            me.grouper = grouper ? me.getSorters().decodeSorter(grouper, 'Ext.util.Grouper') : null;
        }
        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.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.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('load', me, [], true);
                        me.fireEvent('refresh', me);
                    }
                } 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) {
        return this.getData().hasRange(start, end);
    },
    
    pageCached: function(page) {
        return this.getData().hasPage(page);
    },
    
    pagePending: function(page) {
        return !!this.pageRequests[page];
    },
    
    rangeSatisfied: function(start, end) {
        return this.rangeCached(start, end);
    },
    
    onRangeAvailable: function(options) {
        var me = this,
            totalCount = me.getTotalCount(),
            start = options.prefetchStart,
            end = (options.prefetchEnd > totalCount - 1) ? totalCount - 1 : options.prefetchEnd,
            range;
        end = Math.max(0, end);
        
        if (start > end) {
            Ext.log({
                level: 'warn',
                msg: 'Start (' + start + ') was greater than end (' + end + ') for the range of records requested (' + start + '-' + options.prefetchEnd + ')' + (this.storeId ? ' from store "' + this.storeId + '"' : '')
            });
        }
        
        range = me.getData().getRange(start, end + 1);
        if (options.fireEvent !== false) {
            me.fireEvent('guaranteedrange', range, start, end, options);
        }
        if (options.callback) {
            options.callback.call(options.scope || me, range, start, end, options);
        }
    },
    
    guaranteeRange: function(start, end, callback, scope, options) {
        options = Ext.apply({
            callback: callback,
            scope: scope
        }, options);
        this.getRange(start, end + 1, options);
    },
    
    prefetchRange: function(start, end) {
        var me = this,
            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) {
        this.getData().clear();
        this.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,|]/,
    applyParamOrder: function(paramOrder) {
        if (Ext.isString(paramOrder)) {
            paramOrder = paramOrder.split(this.paramOrderRe);
        }
        return paramOrder;
    },
    updateApi: function() {
        this.methodsResolved = false;
    },
    updateDirectFn: function() {
        this.methodsResolved = false;
    },
    resolveMethods: function() {
        var me = this,
            fn = me.getDirectFn(),
            api = me.getApi(),
            Manager = Ext.direct.Manager,
            method;
        if (fn) {
            me.setDirectFn(method = Manager.parseMethod(fn));
            if (!Ext.isFunction(method)) {
                Ext.raise('Cannot resolve directFn ' + fn);
            }
        }
        if (api) {
            for (fn in api) {
                if (api.hasOwnProperty(fn)) {
                    method = api[fn];
                    api[fn] = Manager.parseMethod(method);
                    if (!Ext.isFunction(api[fn])) {
                        Ext.raise('Cannot resolve Direct api ' + fn + ' method ' + method);
                    }
                }
            }
        }
        me.methodsResolved = true;
    },
    doRequest: function(operation) {
        var me = this,
            writer, request, action, params, args, api, fn, callback;
        if (!me.methodsResolved) {
            me.resolveMethods();
        }
        request = me.buildRequest(operation);
        action = request.getAction();
        api = me.getApi();
        if (api) {
            fn = api[action];
        }
        fn = fn || me.getDirectFn();
        
        if (!fn) {
            Ext.raise('No Ext Direct function specified for this proxy');
        }
        
        writer = me.getWriter();
        if (writer && operation.allowWrite()) {
            request = writer.write(request);
        }
        
        
        
        
        
        
        
        
        if (action === 'read') {
            params = request.getParams();
        } else {
            params = request.getJsonData();
        }
        args = fn.directCfg.method.getArgs({
            params: params,
            paramOrder: me.getParamOrder(),
            paramsAsHash: me.getParamsAsHash(),
            metadata: me.getMetadata(),
            callback: me.createRequestCallback(request, operation),
            scope: me
        });
        request.setConfig({
            args: args,
            directFn: fn
        });
        fn.apply(window, args);
        
        
        
        return request;
    },
    
    applyEncoding: Ext.identityFn,
    createRequestCallback: function(request, operation) {
        var me = this;
        return function(data, event) {
            me.processResponse(event.status, operation, request, event);
        };
    },
    
    extractResponseData: function(response) {
        return Ext.isDefined(response.result) ? response.result : response.data;
    },
    
    setException: function(operation, response) {
        operation.setException(response.message);
    },
    
    buildUrl: function() {
        return '';
    }
});


Ext.define('Ext.data.DirectStore', {
    
    extend: Ext.data.Store,
    alias: 'store.direct',
    
    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] = Ext.bind(me.handleResponse, me, [
            request
        ], true);
        me.loadScript(request);
        return request;
    },
    
    abort: function(request) {
        var me = this,
            requests = me.requests,
            key;
        if (request) {
            if (!request.id) {
                request = requests[request];
            }
            me.handleAbort(request);
        } else {
            for (key in requests) {
                if (requests.hasOwnProperty(key)) {
                    me.abort(requests[key]);
                }
            }
        }
    },
    
    setupErrorHandling: function(request) {
        request.script.onerror = Ext.bind(this.handleError, this, [
            request
        ]);
    },
    
    handleAbort: function(request) {
        request.errorType = 'abort';
        this.handleResponse(null, request);
    },
    
    handleError: function(request) {
        request.errorType = 'error';
        this.handleResponse(null, request);
    },
    
    cleanupErrorHandling: function(request) {
        request.script.onerror = null;
    },
    
    handleTimeout: function(request) {
        request.errorType = 'timeout';
        this.handleResponse(null, request);
    },
    
    handleResponse: function(result, request) {
        var success = true,
            globalEvents = Ext.GlobalEvents;
        if (request.timeout) {
            clearTimeout(request.timeout);
        }
        delete this[request.callbackName];
        delete this.requests[request.id];
        this.cleanupErrorHandling(request);
        Ext.fly(request.script).destroy();
        if (request.errorType) {
            success = false;
            Ext.callback(request.failure, request.scope, [
                request.errorType
            ]);
        } else {
            Ext.callback(request.success, request.scope, [
                result
            ]);
        }
        Ext.callback(request.callback, request.scope, [
            success,
            result,
            request.errorType
        ]);
        if (globalEvents.hasListeners.idle) {
            globalEvents.fireEvent('idle');
        }
    },
    
    createScript: function(url, params, options) {
        var script = document.createElement('script');
        script.setAttribute("src", Ext.urlAppend(url, Ext.Object.toQueryString(params)));
        script.setAttribute("async", true);
        script.setAttribute("type", "text/javascript");
        return script;
    },
    
    loadScript: function(request) {
        Ext.getHead().appendChild(request.script);
    }
});


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


Ext.define('Ext.data.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', {
    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
    },
    
    lazyFill: false,
    fillCount: 0,
    bulkUpdate: 0,
    
    _silentOptions: {
        silent: true
    },
    implicitModel: 'Ext.data.TreeModel',
    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
        ]);
    },
    
    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;
        if (me.needsLocalFilter()) {
            me.doFilter(node);
        }
        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);
        }
    },
    fireChangeEvent: function(record) {
        return !!this.byIdMap[record.id];
    },
    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) {
        var isNode = model.prototype.isNode;
        
        Ext.data.NodeInterface.decorate(model);
        
        
        if (!isNode && !this.changingProxy) {
            this.getProxy().getReader().buildExtractors(true);
        }
    },
    onFilterEndUpdate: function(filters) {
        var me = this,
            length = filters.length,
            root = me.getRoot(),
            childNodes, childNode, filteredNodes, i;
        if (!me.getRemoteFilter()) {
            if (length) {
                me.doFilter(root);
            } else {
                root.cascadeBy({
                    after: function(node) {
                        
                        
                        node.set('visible', true, me._silentOptions);
                    }
                });
            }
            if (length) {
                filteredNodes = [];
                childNodes = root.childNodes;
                for (i = 0 , length = childNodes.length; i < length; i++) {
                    childNode = childNodes[i];
                    if (childNode.get('visible')) {
                        filteredNodes.push(childNode);
                    }
                }
            } else {
                filteredNodes = root.childNodes;
            }
            me.onNodeFilter(root, filteredNodes);
            root.fireEvent('filterchange', root, filteredNodes);
            me.fireEvent('filterchange', me, filters);
            
            
            me.suppressNextFilter = true;
            me.callParent([
                filters
            ]);
            me.suppressNextFilter = false;
        } else {
            me.callParent([
                filters
            ]);
        }
    },
    
    onNodeFilter: function(root, childNodes) {
        var me = this,
            data = me.getData(),
            toAdd = [];
        
        if (me.getRootVisible()) {
            if (childNodes.length) {
                toAdd.push(root);
            } else {
                root.set('visible', false, me._silentOptions);
            }
        }
        me.handleNodeExpand(root, childNodes, toAdd);
        
        
        me.suspendEvents();
        data.splice(0, data.getCount(), toAdd);
        me.resumeEvents();
        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.insert(insertIndex, toAdd);
        }
    },
    
    
    handleNodeExpand: function(parent, records, toAdd) {
        var me = this,
            ln = records ? records.length : 0,
            i, record;
        
        if (parent !== this.getRoot() && !me.isVisible(parent)) {
            return;
        }
        if (ln) {
            
            
            for (i = 0; i < ln; i++) {
                record = records[i];
                
                if (record.get('visible')) {
                    
                    
                    
                    toAdd.push(record);
                    if (record.isExpanded()) {
                        if (record.isLoaded()) {
                            
                            me.handleNodeExpand(record, record.childNodes, toAdd);
                        } else {
                            
                            record.set('expanded', false);
                            record.expand();
                        }
                    }
                }
            }
        }
    },
    
    onNodeCollapse: function(parent, records, callback, scope) {
        var me = this,
            collapseIndex = me.indexOf(parent) + 1,
            lastNodeIndexPlus;
        
        if (me.needsLocalFilter()) {
            records = Ext.Array.filter(records, me.filterVisible);
        }
        
        
        
        
        if (records.length && me.data.contains(records[0])) {
            
            lastNodeIndexPlus = me.indexOfNextVisibleNode(parent);
            
            me.removeAt(collapseIndex, lastNodeIndexPlus - collapseIndex);
        }
        Ext.callback(callback, scope);
    },
    
    indexOfNextVisibleNode: function(node) {
        var result;
        while (node.parentNode) {
            
            for (result = node.nextSibling; result && !result.get('visible'); result = result.nextSibling) {}
            
            
            if (result) {
                return this.indexOf(result);
            }
            
            node = node.parentNode;
        }
        
        return this.getCount();
    },
    
    filterNew: function(item) {
        
        
        return !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);
    },
    getUpdatedRecords: function() {
        return Ext.Array.filter(Ext.Object.getValues(this.byIdMap), this.filterUpdated);
    },
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    beforeNodeRemove: function(parentNode, childNodes) {
        if (!Ext.isArray(childNodes)) {
            childNodes = [
                childNodes
            ];
        }
        var me = this,
            len = childNodes.length,
            i, startNode;
        
        for (i = 0; !startNode && i < len; i++) {
            if (childNodes[i].get('visible')) {
                startNode = childNodes[i];
            }
        }
        
        if (startNode) {
            me.startRemoveIndex = me.indexOf(childNodes[0]);
            me.lastRemoveIndexPlusOne = me.indexOfNextVisibleNode(childNodes[childNodes.length - 1]);
        } else {
            me.startRemoveIndex = -1;
            me.lastRemoveIndexPlusOne = 0;
        }
    },
    
    
    
    
    
    afterDrop: Ext.emptyFn,
    
    
    onNodeRemove: function(parentNode, childNodes, isMove) {
        var me = this,
            
            
            
            
            removed = me.removedNodes,
            len = childNodes.length,
            startRemoveIndex = me.startRemoveIndex,
            lastRemoveIndexPlusOne = me.lastRemoveIndexPlusOne,
            i;
        
        
        me.suspendAutoSync();
        
        
        
        
        
        if (startRemoveIndex !== -1) {
            me.removeIsMove = isMove;
            me.removeAt(startRemoveIndex, lastRemoveIndexPlusOne - startRemoveIndex);
            me.removeIsMove = false;
        }
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        for (i = 0; i < len; i++) {
            childNodes[i].cascadeBy(function(node) {
                
                me.unregisterNode(node);
                
                
                
                if (removed && !isMove) {
                    
                    
                    if (!node.phantom && !node.erasing && !me.loading) {
                        
                        
                        node.removedFrom = me.indexOf(node);
                        removed.push(node);
                        
                        
                        me.needsSync = true;
                    }
                }
            });
        }
        me.resumeAutoSync();
    },
    
    onNodeAppend: function(parent, node, index) {
        this.onNodeInsert(parent, node, index);
    },
    
    onNodeInsert: function(parent, node, index) {
        var me = this,
            data = node.raw || node.data,
            
            
            
            
            removed = me.removedNodes,
            refNode, sibling, storeReader, nodeProxy, nodeReader, reader, dataRoot;
        if (parent && me.needsLocalFilter()) {
            me.doFilter(parent);
        }
        me.beginUpdate();
        
        if (me.isVisible(node)) {
            if (index === 0 || !node.previousSibling) {
                refNode = parent;
            } else {
                
                for (sibling = node.previousSibling; sibling && !sibling.get('visible'); sibling = sibling.previousSibling) {}
                
                while (sibling.isExpanded() && sibling.lastChild) {
                    sibling = sibling.lastChild;
                }
                refNode = sibling;
            }
            
            me.insert(me.indexOf(refNode) + 1, node);
            if (!node.isLeaf() && node.isExpanded()) {
                if (node.isLoaded()) {
                    
                    me.onNodeExpand(node, node.childNodes);
                } else if (!me.fillCount) {
                    
                    
                    
                    
                    node.set('expanded', false);
                    node.expand();
                }
            }
        }
        
        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,
            children, length, i;
        
        me.byIdMap[node.id] = node;
        if (includeChildren === true) {
            children = node.childNodes;
            length = children.length;
            for (i = 0; i < length; i++) {
                me.registerNode(children[i], true);
            }
        }
    },
    
    unregisterNode: function(node, includeChildren) {
        var me = this,
            children, length, i;
        delete me.byIdMap[node.id];
        if (includeChildren === true) {
            children = node.childNodes;
            length = children.length;
            for (i = 0; i < length; i++) {
                me.unregisterNode(children[i], true);
            }
        }
    },
    onNodeSort: function(node, childNodes) {
        var me = this;
        
        
        me.suspendAutoSync();
        
        
        if ((me.indexOf(node) !== -1 && node.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;
        
        me.byIdMap = {};
        
        me.getTrackRemoved();
        
        
        me.suspendEvent('add', 'remove');
        
        
        if (oldRoot && oldRoot.isModel) {
            
            if (me.getRootVisible()) {
                toRemove = [
                    oldRoot
                ];
            } else {
                toRemove = oldRoot.childNodes;
            }
            me.beforeNodeRemove(null, toRemove);
            oldRoot.set('root', false);
            me.onNodeRemove(null, toRemove);
            oldRoot.fireEvent('remove', null, oldRoot, false);
            oldRoot.fireEvent('rootchange', null);
            oldRoot.clearListeners();
            oldRoot.store = oldRoot.treeStore = null;
        }
        me.getData().clear();
        
        
        if (newRoot) {
            
            if (newRoot.fireEventArgs('beforeappend', [
                null,
                newRoot
            ]) === false) {
                newRoot = null;
            } else {
                
                oldOwner = newRoot.parentNode;
                if (oldOwner) {
                    
                    
                    
                    
                    if (!oldOwner.removeChild(newRoot, false, false, oldOwner.getTreeStore() === me)) {
                        return;
                    }
                }
                
                else if ((oldOwner = newRoot.getTreeStore()) && oldOwner !== me && newRoot === oldOwner.getRoot()) {
                    oldOwner.setRoot(null);
                }
                
                
                
                newRoot.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;
            }
        }
        me.fireEvent('rootchange', newRoot, oldRoot);
        
        if (newRoot && (me.getAutoLoad() || newRoot.isExpanded())) {
            
            if (newRoot.isLoaded()) {
                me.onNodeExpand(newRoot, newRoot.childNodes);
                me.fireEvent('datachanged', me);
                me.fireEvent('refresh', me);
            } else 
            {
                newRoot.data.expanded = false;
                newRoot.expand(false, function() {
                    me.fireEvent('datachanged', me);
                    me.fireEvent('refresh', me);
                });
            }
        } else if (!initial) {
            me.fireEvent('datachanged', me);
            me.fireEvent('refresh', me);
        }
        
        me.resumeEvent('add', 'remove');
    },
    
    
    getNodeById: function(id) {
        return this.byIdMap[id] || null;
    },
    
    findNode: function(property, value, startsWith, endsWith, ignoreCase) {
        if (Ext.isEmpty(value, false)) {
            return null;
        }
        
        if (value === this.model.idProperty && arguments.length < 3) {
            return this.byIdMap[value];
        }
        var regex = Ext.String.createRegex(value, startsWith, endsWith, ignoreCase),
            result = null;
        Ext.Object.eachValue(this.byIdMap, function(node) {
            if (node && regex.test(node.get(property))) {
                result = node;
                return false;
            }
        });
        return result;
    },
    
    load: function(options) {
        var node = options && options.node;
        
        
        if (!node & !(node = this.getRoot())) {
            node = this.setRoot({
                expanded: true
            });
            return;
        }
        
        if (node.isLoading()) {
            return;
        }
        return this.callParent([
            options
        ]);
    },
    
    flushLoad: function() {
        var me = this,
            options = me.pendingLoadOptions,
            node, callback, scope,
            clearOnLoad = me.getClearOnLoad(),
            isReload, operation, doClear;
        
        me.clearLoadTask();
        if (!options) {
            return;
        }
        node = options.node || me.getRoot();
        
        
        
        
        
        isReload = node && node.isRoot() && node.isLoaded() && clearOnLoad;
        callback = options.callback;
        scope = options.scope;
        options.params = options.params || {};
        
        
        if (node.data.expanded && !isReload) {
            node.data.loaded = false;
            
            
            if (clearOnLoad) {
                node.data.expanded = false;
            }
            options.callback = function(loadedNodes, operation, success) {
                
                
                
                if (!clearOnLoad) {
                    node.collapse();
                }
                node.expand();
                
                Ext.callback(callback, scope, [
                    loadedNodes,
                    operation,
                    success
                ]);
            };
        }
        
        
        options.id = node.getId();
        options = Ext.apply({
            filters: me.getFilters().items,
            sorters: me.getSorters().items,
            node: options.node || node,
            internalScope: me,
            internalCallback: me.onProxyLoad
        }, options);
        me.lastOptions = Ext.apply({}, options);
        
        options.isReload = isReload;
        operation = me.createOperation('read', options);
        if (me.fireEvent('beforeload', me, operation) !== false) {
            
            
            me.loading = true;
            
            if (isReload) {
                if (me.getClearRemovedOnLoad()) {
                    me.removedNodes.length = 0;
                }
                me.unregisterNode(node, true);
                node.childNodes.length = 0;
                doClear = true;
            }
            
            else if (clearOnLoad) {
                if (me.getTrackRemoved() && me.getClearRemovedOnLoad()) {
                    
                    me.clearRemoved(node);
                }
                node.removeAll(false);
            }
            if (me.loading && node) {
                node.set('loading', true);
            }
            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,
            isReload = options.isReload,
            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 (isReload) {
                me.suspendEvent('add', 'update');
            }
            records = me.fillNode(node, records);
        }
        
        
        
        if (isReload) {
            me.resumeEvent('add', 'update');
            me.callObservers('BeforePopulate');
            me.fireEvent('datachanged', me);
            me.fireEvent('refresh', me);
            me.callObservers('AfterPopulate');
        } else {
            Ext.callback(options.onChildNodesAvailable, scope, args);
        }
        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;
        
        ++me.bulkUpdate;
        if (newNodeCount) {
            me.setupNodes(newNodes);
        }
        if (me.bulkUpdate === 1) {
            node.set('loaded', true);
        } else {
            node.data.loaded = true;
        }
        if (newNodes.length) {
            node.appendChild(newNodes, undefined, true);
        }
        --me.bulkUpdate;
        
        return newNodes;
    },
    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,
            node1, node2, i, filterFn;
        
        if (me.needsLocalFilter()) {
            filterFn = me.getFilters().getFilterFn();
            newNodes[0].set('visible', filterFn(newNodes[0]));
        }
        
        for (i = 1; i < newNodeCount; i++) {
            node1 = newNodes[i];
            node2 = newNodes[i - 1];
            
            if (filterFn) {
                node1.set('visible', filterFn(node1));
            }
            needsIndexSort = node1.data.index !== node2.data.index;
        }
        
        if (performLocalSort) {
            
            me.needsIndexSort = true;
            Ext.Array.sort(newNodes, me.getSortFn());
            me.needsIndexSort = false;
        } else if (needsIndexSort) {
            Ext.Array.sort(newNodes, me.sortByIndex);
        }
    },
    
    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];
            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);
            }
        }
        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: {
        
        getRawRemovedRecords: function() {
            return this.removedNodes;
        },
        
        recordCreator: function(data, Model) {
            return new Model(data);
        },
        doFilter: function(node) {
            var root = this.getRoot(),
                filterFn = this.getFilters().getFilterFn();
            this.filterNodes(root, node, filterFn);
        },
        filterNodes: function(root, node, filterFn) {
            var match = false,
                childNodes = node.childNodes,
                len = childNodes && childNodes.length,
                i;
            if (len) {
                for (i = 0; i < len; ++i) {
                    this.filterNodes(root, childNodes[i], filterFn);
                }
            }
            match = node === root || filterFn(node);
            node.set('visible', match, this._silentOptions);
            return match;
        },
        needsLocalFilter: function() {
            return !this.getRemoteFilter() && this.getFilters().length;
        },
        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;
        
        
        delete this.data.id;
    },
    getValidation: function() {
        return null;
    },
    
    isValid: function() {
        var me = this;
        if (me.syncGeneration !== me.record.generation) {
            me.refresh();
        }
        return !me.dirty;
    },
    
    refresh: function(force) {
        var me = this,
            data = me.data,
            record = me.record,
            fields = record.fields,
            generation = record.generation,
            recordData = record.data,
            sep = record.validationSeparator,
            values = null,
            defaultMessage, currentValue, error, field, item, i, j, jLen, len, msg, val, name;
        if (force || me.syncGeneration !== generation) {
            me.syncGeneration = generation;
            for (i = 0 , len = fields.length; i < len; ++i) {
                field = fields[i];
                name = field.name;
                val = recordData[name];
                defaultMessage = field.defaultInvalidMessage;
                error = 0;
                if (!(name in data)) {
                    
                    
                    
                    data[name] = currentValue = true;
                } else 
                {
                    currentValue = data[name];
                }
                if (field.validate !== Ext.emptyFn) {
                    msg = field.validate(val, sep, 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 (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', {
    extend: Ext.data.reader.Reader,
    alternateClassName: 'Ext.data.XmlReader',
    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) {
        var nodeName = data.nodeName,
            root = this.getRootProperty();
        if (!root || (nodeName && nodeName == root)) {
            return data;
        } else if (Ext.DomQuery.isXml(data)) {
            
            
            
            return Ext.DomQuery.selectNode(root, data);
        }
    },
    
    extractData: function(root, readOptions) {
        var recordName = this.getRecord();
        
        if (!recordName) {
            Ext.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 me = this,
            namespace = me.getNamespace(),
            selector, result;
        selector = field.mapping || ((namespace ? namespace + '|' : '') + field.name);
        if (typeof selector === 'function') {
            result = function(raw) {
                return field.mapping(raw, me);
            };
        } else {
            result = function(raw) {
                return me.getNodeValue(Ext.DomQuery.selectNode(selector, raw));
            };
        }
        return result;
    },
    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) {
                record.phantom = false;
                identifier = record.identifier;
                if (identifier && identifier.isUnique) {
                    id = record.getId();
                } else {
                    id = me.getNextId();
                }
            } else {
                id = record.getId();
            }
            me.setRecord(record, id);
            record.commit();
            ids.push(id);
        }
        me.setIds(ids);
        operation.setSuccessful(true);
    },
    
    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;
        for (; i < length; i++) {
            field = fields[i];
            name = field.name;
            if (field.persist) {
                data[name] = rawData[name];
            }
        }
        
        delete data[model.prototype.idProperty];
        
        if (record.isNode && record.get('depth') === 1) {
            delete data.parentId;
        }
        obj = me.getStorageObject();
        key = me.getRecordKey(id);
        
        me.cache[id] = data;
        
        obj.removeItem(key);
        obj.setItem(key, Ext.encode(data));
    },
    
    removeRecord: function(record) {
        var me = this,
            id = record.getId(),
            records = {},
            i, childNodes;
        records[id] = record;
        me.getStorageObject().removeItem(me.getRecordKey(id));
        delete me.cache[id];
        if (record.childNodes) {
            childNodes = record.childNodes;
            for (i = childNodes.length; i--; ) {
                Ext.apply(records, me.removeRecord(childNodes[i]));
            }
        }
        return records;
    },
    
    getRecordKey: function(id) {
        if (id.isModel) {
            id = id.getId();
        }
        return Ext.String.format("{0}-{1}", this.getId(), id);
    },
    
    getRecordCounterKey: function() {
        return Ext.String.format("{0}-counter", this.getId());
    },
    
    getTreeKey: function() {
        return Ext.String.format("{0}-tree", this.getId());
    },
    
    getIds: function() {
        var me = this,
            ids = (me.getStorageObject().getItem(me.getId()) || "").split(","),
            length = ids.length,
            isString = this.getIdField().isStringField,
            i;
        if (length === 1 && ids[0] === "") {
            ids = [];
        } else {
            for (i = 0; i < length; i++) {
                ids[i] = isString ? ids[i] : +ids[i];
            }
        }
        return ids;
    },
    getIdField: function() {
        return this.getModel().prototype.idField;
    },
    
    setIds: function(ids) {
        var obj = this.getStorageObject(),
            str = ids.join(","),
            id = this.getId();
        obj.removeItem(id);
        if (!Ext.isEmpty(str)) {
            obj.setItem(id, str);
        }
    },
    
    getNextId: function() {
        var me = this,
            obj = me.getStorageObject(),
            key = me.getRecordCounterKey(),
            isString = me.getIdField().isStringField,
            id;
        id = me.idGenerator.generate();
        obj.setItem(key, id);
        if (isString) {
            id = id + '';
        }
        return id;
    },
    
    getTreeData: function() {
        var me = this,
            ids = me.getIds(),
            length = ids.length,
            records = [],
            recordHash = {},
            root = [],
            i = 0,
            Model = me.getModel(),
            idProperty = Model.prototype.idProperty,
            rootLength, record, parent, parentId, children, id;
        for (; i < length; i++) {
            id = ids[i];
            
            record = me.getRecord(id);
            
            records.push(record);
            
            recordHash[id] = record;
            if (!record.parentId) {
                
                root.push(record);
            }
        }
        rootLength = root.length;
        
        Ext.Array.sort(records, me.sortByParentId);
        
        for (i = rootLength; i < length; i++) {
            record = records[i];
            parentId = record.parentId;
            if (!parent || parent[idProperty] !== parentId) {
                
                parent = recordHash[parentId];
                parent.children = children = [];
            }
            
            children.push(record);
        }
        for (i = length; i--; ) {
            record = records[i];
            if (!record.children && !record.leaf) {
                
                record.loaded = true;
            }
        }
        
        for (i = rootLength; i--; ) {
            record = root[i];
            root[i] = new Model(record);
        }
        return root;
    },
    
    sortByParentId: function(node1, node2) {
        return (node1.parentId || 0) - (node2.parentId || 0);
    },
    
    initialize: function() {
        var me = this,
            storageObject = me.getStorageObject(),
            lastId = +storageObject.getItem(me.getRecordCounterKey()),
            id = me.getId();
        storageObject.setItem(id, storageObject.getItem(id) || "");
        if (storageObject.getItem(me.getTreeKey())) {
            me.isHierarchical = true;
        }
        me.idGenerator = new Ext.data.identifier.Sequential({
            seed: lastId ? lastId + 1 : 1
        });
    },
    
    clear: function() {
        var me = this,
            obj = me.getStorageObject(),
            ids = me.getIds(),
            len = ids.length,
            i;
        
        for (i = 0; i < len; i++) {
            obj.removeItem(me.getRecordKey(ids[i]));
        }
        
        obj.removeItem(me.getRecordCounterKey());
        obj.removeItem(me.getTreeKey());
        obj.removeItem(me.getId());
        
        me.cache = {};
    },
    
    getStorageObject: function() {
        
        Ext.raise("The getStorageObject function has not been defined in your Ext.data.proxy.WebStorage subclass");
    }
});



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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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



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


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


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


Ext.define('Ext.dom.Fly', {
    extend: Ext.dom.Element,
    alternateClassName: 'Ext.dom.Element.Fly',
    
    
    
    
    validNodeTypes: {
        1: 1,
        
        9: 1,
        
        11: 1
    },
    
    
    isFly: true,
    constructor: function(dom) {
        this.dom = dom;
        
        
        
        this.el = this;
    },
    attach: function(dom) {
        var me = this;
        if (!dom) {
            return me.detach();
        }
        me.dom = dom;
        
        
        if (!Ext.cache[dom.id]) {
            me.getData().isSynchronized = false;
        }
        return me;
    },
    detach: function() {
        this.dom = null;
    },
    addListener: 
    function() {
        Ext.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 = {};
    
    Fly.cache = flyweights;
    
    Ext.fly = function(dom, named) {
        var fly = null,
            fn = Ext.fly,
            nodeType, data;
        
        named = named || (fn.caller && fn.caller.$name) || '_global';
        dom = Ext.getDom(dom);
        if (dom) {
            nodeType = dom.nodeType;
            
            
            
            
            
            
            if (Fly.prototype.validNodeTypes[nodeType] || (!nodeType && (dom.window == dom))) {
                fly = Ext.cache[dom.id];
                
                if (!fly || fly.dom !== dom) {
                    fly = flyweights[named] || (flyweights[named] = new Fly());
                    fly.dom = dom;
                    data = fly.getData(true);
                    if (data) {
                        data.isSynchronized = false;
                    }
                }
            }
        }
        return fly;
    };
});


Ext.define('Ext.dom.CompositeElementLite', {
    alternateClassName: [
        'Ext.CompositeElementLite'
    ],
    
    isComposite: true,
    
    isLite: true,
    
    
    
    
    
    
    
    statics: {
        
        
        
        
        importElementMethods: function() {
            var Element = Ext.dom.Element,
                prototype = this.prototype;
            Ext.Object.each(Element.prototype, function(name, member) {
                if (typeof member === 'function' && !prototype[name]) {
                    prototype[name] = function() {
                        return this.invoke(name, arguments);
                    };
                }
            });
        }
    },
    constructor: function(elements, 
    skipValidation) {
        
        if (skipValidation) {
            
            
            
            
            this.elements = elements || [];
        } else {
            this.elements = [];
            this.add(elements);
        }
    },
    
    getElement: function(el) {
        
        var fly = this._fly || (this._fly = new Ext.dom.Fly());
        return fly.attach(el);
    },
    
    transformElement: function(el) {
        return Ext.getDom(el);
    },
    
    getCount: function() {
        return this.elements.length;
    },
    
    add: function(els, root) {
        var elements = this.elements,
            i, ln;
        if (!els) {
            return this;
        }
        if (typeof els == "string") {
            els = Ext.fly(root || document).query(els);
        } else if (els.isComposite) {
            els = els.elements;
        } else if (!Ext.isIterable(els)) {
            els = [
                els
            ];
        }
        for (i = 0 , ln = els.length; i < ln; ++i) {
            elements.push(this.transformElement(els[i]));
        }
        return this;
    },
    invoke: function(fn, args) {
        var me = this,
            elements = me.elements,
            ln = elements.length,
            prototype, element, i;
        if (i !== 0) {
            
            
            prototype = (me.isLite ? Ext.dom.Fly : Ext.dom.Element).prototype;
            for (i = 0; i < ln; i++) {
                element = elements[i];
                if (element) {
                    prototype[fn].apply(me.getElement(element), args);
                }
            }
        }
        return me;
    },
    
    item: function(index) {
        var el = this.elements[index],
            out = null;
        if (el) {
            out = this.getElement(el);
        }
        return out;
    },
    
    slice: function(start, end) {
        return Ext.Array.slice(this.elements, start, end);
    },
    
    each: function(fn, scope) {
        var me = this,
            els = me.elements,
            len = els.length,
            i, e;
        for (i = 0; i < len; i++) {
            e = els[i];
            if (e) {
                e = this.getElement(e);
                if (fn.call(scope || e, e, me, i) === false) {
                    break;
                }
            }
        }
        return me;
    },
    
    fill: function(els) {
        var me = this;
        me.elements = [];
        me.add(els);
        return me;
    },
    insert: function(index, nodes) {
        Ext.Array.insert(this.elements, index, nodes);
    },
    
    filter: function(selector) {
        var me = this,
            els = me.elements,
            len = els.length,
            out = [],
            i = 0,
            isFunc = typeof selector == 'function',
            add, el;
        for (; i < len; i++) {
            el = els[i];
            add = false;
            if (el) {
                el = me.getElement(el);
                if (isFunc) {
                    add = selector.call(el, el, me, i) !== false;
                } else {
                    add = el.is(selector);
                }
                if (add) {
                    out.push(me.transformElement(el));
                }
            }
        }
        me.elements = out;
        return me;
    },
    
    indexOf: function(el) {
        return Ext.Array.indexOf(this.elements, this.transformElement(el));
    },
    
    replaceElement: function(el, replacement, domReplace) {
        var index = !isNaN(el) ? el : this.indexOf(el),
            d;
        if (index > -1) {
            replacement = Ext.getDom(replacement);
            if (domReplace) {
                d = this.elements[index];
                d.parentNode.insertBefore(replacement, d);
                Ext.removeNode(d);
            }
            Ext.Array.splice(this.elements, index, 1, replacement);
        }
        return this;
    },
    
    clear: function(removeDom) {
        var me = this,
            els = me.elements,
            i = els.length - 1;
        if (removeDom) {
            for (; i >= 0; i--) {
                Ext.removeNode(els[i]);
            }
        }
        this.elements = [];
    },
    addElements: function(els, root) {
        if (!els) {
            return this;
        }
        if (typeof els === "string") {
            els = Ext.dom.Element.selectorFunction(els, root);
        }
        var yels = this.elements,
            eLen = els.length,
            e;
        for (e = 0; e < eLen; e++) {
            yels.push(Ext.get(els[e]));
        }
        return this;
    },
    
    first: function() {
        return this.item(0);
    },
    
    last: function() {
        return this.item(this.getCount() - 1);
    },
    
    contains: function(el) {
        return this.indexOf(el) != -1;
    },
    
    removeElement: function(keys, removeDom) {
        keys = [].concat(keys);
        var me = this,
            elements = me.elements,
            kLen = keys.length,
            val, el, k;
        for (k = 0; k < kLen; k++) {
            val = keys[k];
            if ((el = (elements[val] || elements[val = me.indexOf(val)]))) {
                if (removeDom) {
                    if (el.dom) {
                        el.destroy();
                    } else {
                        Ext.removeNode(el);
                    }
                }
                Ext.Array.erase(elements, val, 1);
            }
        }
        return me;
    },
    destroy: function() {
        
        return this.invoke('destroy', arguments);
        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.collect = Ext.Function.bind(me.collect, me);
        me.lastTime = Ext.now();
        me.resume();
    },
    
    collect: function() {
        var me = this,
            cache = Ext.cache,
            eid, dom, el, t, isGarbage, tagName;
        
        var collectedIds = [];
        
        for (eid in cache) {
            if (!cache.hasOwnProperty(eid)) {
                
                continue;
            }
            el = cache[eid];
            if (el.skipGarbageCollection) {
                
                continue;
            }
            dom = el.dom;
            
            
            if (!dom) {
                Ext.raise('Missing DOM node in element garbage collection: ' + eid);
            }
            
            try {
                
                
                
                
                
                isGarbage = Ext.isGarbage(dom);
            } catch (e) {
                
                
                
                
                delete cache[eid];
                
                collectedIds.push('#' + el.id);
                
                
                continue;
            }
            if (isGarbage) {
                if (el && el.dom) {
                    
                    tagName = el.dom.tagName;
                    
                    el.collect();
                    
                    collectedIds.push((tagName ? tagName : '') + '#' + el.id);
                }
            }
        }
        
        
        
        if (Ext.isIE9m) {
            t = {};
            for (eid in cache) {
                if (cache.hasOwnProperty(eid)) {
                    t[eid] = cache[eid];
                }
            }
            Ext.cache = Ext.dom.Element.cache = t;
        }
        
        me.lastTime = Ext.now();
        
        return collectedIds;
    },
    
    
    pause: function() {
        clearTimeout(this.timerId);
    },
    
    resume: function() {
        var me = this,
            lastTime = me.lastTime;
        if (Ext.enableGarbageCollector && (Ext.now() - lastTime > me.interval)) {
            me.collect();
        }
        me.timerId = Ext.interval(me.collect, me.interval);
    }
});


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


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


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


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


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


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


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


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


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


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


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


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


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
    },
    durationProperties: {
        'transition-duration': true,
        'transition-delay': true,
        'animation-duration': true,
        'animation-delay': true
    },
    angleProperties: {
        rotate: true,
        rotateX: true,
        rotateY: true,
        rotateZ: true,
        skew: true,
        skewX: true,
        skewY: true
    },
    lengthUnitRegex: /([a-z%]*)$/,
    DEFAULT_UNIT_LENGTH: 'px',
    DEFAULT_UNIT_ANGLE: 'deg',
    DEFAULT_UNIT_DURATION: 'ms',
    formattedNameCache: {},
    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, 'important');
                    }
                }
            }
        }
        return this;
    },
    applyStyles: function(styles) {
        var id, element, elementStyle, properties, name, value;
        for (id in styles) {
            if (styles.hasOwnProperty(id)) {
                element = document.getElementById(id);
                if (!element) {
                    
                    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, 'important');
                        }
                    }
                }
            }
        }
        return this;
    },
    formatName: function(name) {
        var cache = this.formattedNameCache,
            formattedName = cache[name];
        if (!formattedName) {
            if ((Ext.os.is.Tizen || !Ext.feature.has.CssTransformNoPrefix) && this.prefixedProperties[name]) {
                formattedName = this.vendorPrefix + name;
            } else {
                formattedName = name;
            }
            cache[name] = formattedName;
        }
        return formattedName;
    },
    formatValue: function(value, name) {
        var type = typeof value,
            lengthUnit = this.DEFAULT_UNIT_LENGTH,
            transformMethods, method, i, ln, transformValues, values, unit;
        if (value === null) {
            return '';
        }
        if (type == 'string') {
            if (this.lengthProperties[name]) {
                unit = value.match(this.lengthUnitRegex)[1];
                if (unit.length > 0) {
                    
                    if (unit !== lengthUnit) {
                        Ext.Logger.error("Length unit: '" + unit + "' in value: '" + value + "' of property: '" + name + "' is not " + "valid for animation. Only 'px' is allowed");
                    }
                } else 
                {
                    return value + lengthUnit;
                }
            }
            return value;
        } else if (type == 'number') {
            if (value == 0) {
                return '0';
            }
            if (this.lengthProperties[name]) {
                return value + lengthUnit;
            }
            if (this.angleProperties[name]) {
                return value + this.DEFAULT_UNIT_ANGLE;
            }
            if (this.durationProperties[name]) {
                return value + this.DEFAULT_UNIT_DURATION;
            }
        } else if (name === 'transform') {
            transformMethods = this.transformMethods;
            transformValues = [];
            for (i = 0 , ln = transformMethods.length; i < ln; i++) {
                method = transformMethods[i];
                transformValues.push(method + '(' + this.formatValue(value[method], method) + ')');
            }
            return transformValues.join(' ');
        } else if (Ext.isArray(value)) {
            values = [];
            for (i = 0 , ln = value.length; i < ln; i++) {
                values.push(this.formatValue(value[i], name));
            }
            return (values.length > 0) ? values.join(', ') : 'none';
        }
        return value;
    }
});


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


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


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


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



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


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


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


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


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


Ext.define('Ext.fx.layout.card.ScrollReveal', {
    extend: Ext.fx.layout.card.Scroll,
    alias: 'fx.layout.card.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.innerElement;
            containerSize = containerElement.getSize();
            xy = this.calculateXY(containerSize);
            animConfig = {
                easing: this.getEasing(),
                duration: this.getDuration()
            };
            outTranslate = outItem.setTranslatable(true).getTranslatable();
            inTranslate = inItem.setTranslatable(true).getTranslatable();
            outTranslate.getWrapper().dom.style.setProperty('z-index', '100', 'important');
            outTranslate.translate({
                x: 0,
                y: 0
            });
            inTranslate.translate({
                x: 0,
                y: 0
            });
            inItem.show();
            outTranslate.on({
                animationend: 'onOutAnimationEnd',
                scope: this
            });
            outTranslate.translateAnimated({
                x: xy.x,
                y: xy.y
            }, animConfig);
            controller.pause();
        }
    },
    onOutAnimationEnd: function() {
        this.outItem.getTranslatable().getWrapper().dom.style.removeProperty('z-index');
        
        this.currentEventController.resume();
    }
});


Ext.define('Ext.fx.runner.CssAnimation', {
    extend: Ext.fx.runner.Css,
    constructor: function() {
        this.runningAnimationsMap = {};
        this.elementEndStates = {};
        this.animationElementMap = {};
        this.keyframesRulesCache = {};
        this.uniqueId = 0;
        return this.callParent(arguments);
    },
    attachListeners: function() {
        this.listenersAttached = true;
        Ext.getWin().on({
            animationstart: 'onAnimationStart',
            animationend: 'onAnimationEnd',
            scope: this
        });
    },
    onAnimationStart: function(e) {
        var name = e.browserEvent.animationName,
            elementId = this.animationElementMap[name],
            animation = this.runningAnimationsMap[elementId][name],
            elementEndStates = this.elementEndStates,
            elementEndState = elementEndStates[elementId],
            data = {};
        
        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,
        
        floated: 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];
                    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.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() {
        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);
    },
    
    removeItem: Ext.emptyFn,
    updateFloated: function(floated) {
        var me = this,
            el = me.element,
            placeholder = me.placeholder,
            node, wasExpanded;
        if (floated) {
            placeholder = el.clone(false, true);
            
            placeholder.id += '-placeholder';
            
            me.placeholder = Ext.get(placeholder);
            me.wasExpanded = me.getExpanded();
            me.setExpanded(true);
            el.dom.parentNode.insertBefore(placeholder, el.dom);
            me.floater = me.createFloater();
        }
        
        else if (placeholder) {
            wasExpanded = me.wasExpanded;
            node = me.getNode();
            me.setExpanded(wasExpanded);
            if (!wasExpanded && node.isExpanded()) {
                
                
                me.preventAnimation = true;
                node.collapse();
                me.preventAnimation = false;
            }
            me.floater.remove(me, false);
            
            placeholder.dom.parentNode.insertBefore(el.dom, placeholder.dom);
            placeholder.destroy();
            me.floater.destroy();
            me.placeholder = me.floater = null;
            me.floatedByHover = false;
        }
    },
    
    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) {
            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());
        },
        
        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();
                }
                if (info.select) {
                    owner.setSelection(me.getNode());
                }
            }
        },
        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);
            }
        }
    }
});


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.list.TreeItem', {
    extend: Ext.list.AbstractTreeItem,
    xtype: 'treelistitem',
    collapsedCls: Ext.baseCSSPrefix + 'treelist-item-collapsed',
    expandedCls: Ext.baseCSSPrefix + 'treelist-item-expanded',
    floatedCls: Ext.baseCSSPrefix + 'treelist-item-floated',
    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;
        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() {
        this.updateExpandCls();
    },
    updateExpanded: function() {
        this.updateExpandCls();
    },
    updateFloated: function(floated, wasFloated) {
        var me = this;
        me.callParent([
            floated,
            wasFloated
        ]);
        me.element.toggleCls(me.floatedCls, floated);
        me.toolElement.toggleCls(me.floatedToolCls, floated);
    },
    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
            ]);
        }
    }
});

Ext.define('Ext.overrides.list.TreeItem', {
    override: 'Ext.list.TreeItem',
    createFloater: function() {
        var me = this,
            owner = me.getOwner(),
            ui = owner.getUi(),
            cls = Ext.baseCSSPrefix + 'treelist',
            floater;
        if (ui) {
            cls += ' ' + cls + '-' + ui;
        }
        me.floater = floater = new Ext.Container({
            cls: cls + ' ' + Ext.baseCSSPrefix + 'treelist-floater',
            width: 200,
            top: 0,
            listeners: {
                element: 'element',
                click: function(e) {
                    return owner.onClick(e);
                }
            }
        });
        Ext.Viewport.add(floater);
        floater.add(me);
        floater.alignTo(me.getToolElement(), 'tl-tr');
        return floater;
    },
    runAnimation: function(animation) {
        return this.itemContainer.animate(animation);
    },
    stopAnimation: function(animation) {
        animation.end();
    }
});


Ext.define('Ext.list.Tree', {
    extend: Ext.Widget,
    xtype: 'treelist',
    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',
            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: {
        
        defaults: {
            xtype: 'treelistitem'
        },
        highlightPath: null,
        iconSize: null,
        indent: null,
        micro: null,
        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());
    },
    beforeLayout: function() {
        
        this.syncIconSize();
    },
    destroy: function() {
        var me = this;
        me.destroying = true;
        
        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) {
            
            
            
            
            for (c = wasOver; c; c = this.getItem(node.parentNode)) {
                node = c.getNode();
                if (map[node.internalId]) {
                    break;
                }
                c.setOver(0);
            }
        }
    },
    applySelection: function(selection, oldSelection) {
        var store = this.getStore();
        if (!store) {
            selection = null;
        }
        if (selection && selection.get('selectable') === false) {
            selection = oldSelection;
        }
        return selection;
    },
    updateSelection: function(selection, oldSelection) {
        var me = this,
            item;
        if (!me.destroying) {
            
            
            item = me.getItem(oldSelection);
            if (item) {
                item.setSelected(false);
            }
            item = me.getItem(selection);
            if (item) {
                item.setSelected(true);
            }
            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.getAutoDestroy()) {
                oldStore.destroy();
            } else {
                me.storeListeners.destroy();
            }
            me.removeRoot();
            me.storeListeners = null;
        }
        if (store) {
            me.storeListeners = store.on({
                destroyable: true,
                scope: me,
                nodeappend: me.onNodeAppend,
                nodecollapse: me.onNodeCollapse,
                nodeexpand: me.onNodeExpand,
                nodeinsert: me.onNodeInsert,
                noderemove: me.onNodeRemove,
                rootchange: me.onRootChange,
                update: me.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 el = this.element,
            uiPrefix = this.uiPrefix;
        if (oldValue) {
            el.removeCls(uiPrefix + oldValue);
        }
        if (ui) {
            el.addCls(uiPrefix + ui);
        }
        
        delete this.iconSize;
        this.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 item = Ext.create(this.getItemConfig(node, parent)),
                toolEl;
            if (parent.isRootListItem) {
                toolEl = item.getToolElement();
                if (toolEl) {
                    this.toolsElement.appendChild(toolEl);
                    toolEl.dom.setAttribute('data-recordId', node.internalId);
                    toolEl.isTool = true;
                }
            }
            return (this.itemMap[node.internalId] = 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;
        },
        floatItem: function(item, byHover) {
            var me = this,
                floater;
            if (item.getFloated()) {
                return;
            }
            me.unfloatAll();
            me.activeFloater = floater = item;
            me.floatedByHover = byHover;
            item.setFloated(true);
            if (byHover) {
                item.getToolElement().on('mouseleave', me.checkForMouseLeave, me);
                floater.element.on('mouseleave', me.checkForMouseLeave, me);
            } else {
                Ext.on('mousedown', me.checkForOutsideClick, me);
            }
        },
        
        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.fromElement(e.getTarget());
            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();
                }
            }
        },
        
        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) {
            this.removeRoot();
            if (root) {
                this.createRootItem(root);
            }
            this.updateLayout();
        },
        
        removeItem: function(node) {
            var map = this.itemMap;
            if (map) {
                delete map[node.internalId];
            }
        },
        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) {
                    floater.element.un('mouseleave', me.checkForMouseLeave, me);
                } else {
                    Ext.un('mousedown', me.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.Accessible', {
    extend: Ext.Mixin,
    mixinConfig: {
        id: 'accessible'
    },
    
    
    
    config: {
        
        ariaAttributes: {
            $value: null,
            lazy: true
        }
    },
    
    
    privates: {
        
        getAriaLabelEl: function(selector) {
            var ids = [],
                refHolder, i, len, cmp, result;
            if (selector) {
                if (Ext.isFunction(selector)) {
                    return selector.call(this);
                } else {
                    if (!Ext.isArray(selector)) {
                        selector = [
                            selector
                        ];
                    }
                    refHolder = this.lookupReferenceHolder();
                    if (refHolder) {
                        for (i = 0 , len = selector.length; i < len; i++) {
                            cmp = refHolder.lookupReference(selector[i]);
                            if (cmp) {
                                ids.push(cmp.ariaEl.id);
                            }
                        }
                    }
                }
            }
            return ids.length ? ids.join(' ') : null;
        }
    }
});


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


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



Ext.define('Ext.mixin.Selectable', {
    extend: Ext.Mixin,
    mixinConfig: {
        id: 'selectable',
        after: {
            updateStore: 'updateStore'
        }
    },
    
    
    config: {
        
        disableSelection: null,
        
        mode: 'SINGLE',
        
        allowDeselect: false,
        
        lastSelected: null,
        
        lastFocused: null,
        
        deselectOnContainerClick: true,
        
        selection: null,
        twoWayBindable: {
            selection: 1
        },
        publishes: {
            selection: 1
        }
    },
    modes: {
        SINGLE: true,
        SIMPLE: true,
        MULTI: true
    },
    selectableEventHooks: {
        add: 'onSelectionStoreAdd',
        remove: 'onSelectionStoreRemove',
        update: 'onSelectionStoreUpdate',
        clear: {
            fn: 'onSelectionStoreClear',
            priority: 1000
        },
        load: 'refreshSelection',
        refresh: 'refreshSelection'
    },
    constructor: function() {
        this.selected = new Ext.util.MixedCollection();
        this.callParent(arguments);
    },
    initSelectable: function() {
        this.publishState('selection', this.getSelection());
    },
    
    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,
            selections = me.getStore().getRange();
        me.deselect(selections, supress);
        me.selected.clear();
        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) {
        var me = this,
            selected = me.selected;
        if (me.getDisableSelection()) {
            return;
        }
        
        
        if (me.isSelected(record)) {
            return;
        }
        if (selected.getCount() > 0) {
            me.deselect(me.getLastSelected(), suppressEvent);
        }
        selected.add(record);
        me.setLastSelected(record);
        me.onItemSelect(record, suppressEvent);
        me.setLastFocused(record);
        if (!suppressEvent) {
            me.fireSelectionChange([
                record
            ]);
        }
    },
    
    doMultiSelect: function(records, keepExisting, suppressEvent) {
        if (records === null || this.getDisableSelection()) {
            return;
        }
        records = !Ext.isArray(records) ? [
            records
        ] : records;
        var me = this,
            selected = me.selected,
            ln = records.length,
            change = false,
            i = 0,
            record;
        if (!keepExisting && selected.getCount() > 0) {
            change = true;
            me.deselect(me.getSelections(), true);
        }
        for (; i < ln; i++) {
            record = records[i];
            if (keepExisting && me.isSelected(record)) {
                
                continue;
            }
            change = true;
            me.setLastSelected(record);
            selected.add(record);
            if (!suppressEvent) {
                me.setLastFocused(record);
            }
            me.onItemSelect(record, suppressEvent);
        }
        if (change && !suppressEvent) {
            this.fireSelectionChange(records);
        }
    },
    
    deselect: function(records, suppressEvent) {
        var me = this;
        if (me.getDisableSelection()) {
            return;
        }
        records = Ext.isArray(records) ? records : [
            records
        ];
        var selected = me.selected,
            change = false,
            i = 0,
            store = me.getStore(),
            ln = records.length,
            record;
        for (; i < ln; i++) {
            record = records[i];
            if (typeof record === 'number') {
                record = store.getAt(record);
            }
            if (selected.remove(record)) {
                if (me.getLastSelected() == record) {
                    me.setLastSelected(selected.last());
                }
                change = true;
            }
            if (record) {
                me.onItemDeselect(record, suppressEvent);
            }
        }
        if (change && !suppressEvent) {
            me.fireSelectionChange(records);
        }
    },
    
    updateLastFocused: function(newRecord, oldRecord) {
        this.onLastFocusChanged(oldRecord, newRecord);
    },
    fireSelectionChange: function(records) {
        var me = this;
        me.changingSelection = true;
        me.setSelection(me.getLastSelected() || null);
        me.changingSelection = false;
        me.fireAction('selectionchange', [
            me,
            records
        ], 'getSelections');
    },
    
    getSelections: function() {
        return this.selected.getRange();
    },
    
    isSelected: function(record) {
        record = Ext.isNumber(record) ? this.getStore().getAt(record) : record;
        return this.selected.indexOf(record) !== -1;
    },
    
    hasSelection: function() {
        return this.selected.getCount() > 0;
    },
    
    refreshSelection: function() {
        var me = this,
            selections = me.getSelections();
        me.deselectAll(true);
        if (selections.length) {
            me.select(selections, false, true);
        }
    },
    
    
    
    onSelectionStoreRemove: function(store, records) {
        var me = this,
            selected = me.selected,
            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.selected.getCount();
    },
    onSelectionStoreAdd: Ext.emptyFn,
    onSelectionStoreUpdate: Ext.emptyFn,
    onItemSelect: Ext.emptyFn,
    onItemDeselect: Ext.emptyFn,
    onLastFocusChanged: Ext.emptyFn,
    onEditorKey: Ext.emptyFn
}, function() {});











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


Ext.define('Ext.plugin.LazyItems', {
    extend: Ext.plugin.Abstract,
    alias: 'plugin.lazyitems',
    init: function(comp) {
        this.callParent(arguments);
        if (this.items) {
            
            if (this.eagerInstantiation) {
                this.items = comp.prepareItems(this.items);
            }
        }
        
        comp.beforeRender = Ext.Function.createInterceptor(comp.beforeRender, this.beforeComponentRender, this);
    },
    
    beforeComponentRender: function() {
        this.cmp.add(this.items);
        
        this.cmp.beforeComponentRender = null;
    }
});



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


Ext.define('Ext.util.DelimitedValue', {
    
    dateFormat: 'C',
    
    delimiter: '\t',
    
    lineBreak: '\n',
    
    quote: '"',
    parseREs: {},
    quoteREs: {},
    lineBreakRe: /\r?\n/g,
    constructor: function(config) {
        if (config) {
            Ext.apply(this, config);
        }
    },
    
    decode: function(input, delimiter) {
        var me = this,
            
            
            delim = (delimiter || me.delimiter),
            row = [],
            result = [
                row
            ],
            quote = me.quote,
            quoteREs = me.quoteREs,
            parseREs = me.parseREs,
            
            
            parseRE = parseREs[delim] || (parseREs[delim] = new RegExp(
            "(\\" + delim + "|\\r?\\n|\\r|^)" + 
            "(?:\\" + quote + "([^\\" + quote + "]*(?:\\" + quote + "\\" + quote + "[^\\" + quote + "]*)*)\\" + quote + "|" + 
            "([^\"\\" + delim + "\\r\\n]*))", "gi")),
            dblQuoteRE = quoteREs[quote] || (quoteREs[quote] = new RegExp('\\' + quote + '\\' + quote, 'g')),
            arrMatches, strMatchedDelimiter, strMatchedValue;
        
        
        while (arrMatches = parseRE.exec(input)) {
            strMatchedDelimiter = arrMatches[1];
            
            
            
            
            if (strMatchedDelimiter.length && strMatchedDelimiter !== delim) {
                
                
                result.push(row = []);
            }
            
            
            
            if (arrMatches[2]) {
                
                
                strMatchedValue = arrMatches[2].replace(dblQuoteRE, '"');
            } else {
                
                strMatchedValue = arrMatches[3];
            }
            row.push(strMatchedValue);
        }
        return result;
    },
    
    encode: function(input, delimiter) {
        var me = this,
            delim = delimiter || me.delimiter,
            dateFormat = me.dateFormat,
            quote = me.quote,
            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) {
                        
                        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.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) {
                if (me._timer) {
                    clearTimeout(me._timer);
                    me._timer = null;
                }
                me.el.setAttribute('xdata', Ext.encode(me.data));
                me.el.save(me.id);
            } else if (!me._timer) {
                me._timer = Ext.defer(me._flushFn, delay);
            }
        },
        clear: function() {
            var me = this;
            me.data = {};
            me._keys = null;
            me.save(me.flushDelay);
        },
        getItem: function(key) {
            var data = this.data;
            return (key in data) ? data[key] : null;
        },
        removeItem: function(key) {
            var me = this,
                keys = me._keys,
                data = me.data;
            if (key in data) {
                delete data[key];
                if (keys) {
                    if (me.lazyKeys) {
                        me._keys = null;
                    } else {
                        Ext.Array.remove(keys, key);
                    }
                }
                me.save(me.flushDelay);
            }
        },
        setItem: function(key, value) {
            var me = this,
                data = me.data,
                keys = me._keys;
            if (keys && !(key in data)) {
                keys.push(key);
            }
            data[key] = value;
            me.save(me.flushDelay);
        }
    });
});


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



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


Ext.define('Ext.util.TextMetrics', {
    statics: {
        shared: null,
        
        measure: function(el, text, fixedWidth) {
            var me = this,
                shared = me.shared;
            if (!shared) {
                shared = me.shared = new me(el, fixedWidth);
            }
            shared.bind(el);
            shared.setFixedWidth(fixedWidth || 'auto');
            return shared.getSize(text);
        },
        
        destroy: function() {
            var me = this;
            Ext.destroy(me.shared);
            me.shared = null;
        }
    },
    
    constructor: function(bindTo, fixedWidth) {
        var me = this,
            measure = Ext.getBody().createChild({
                
                
                'data-sticky': true,
                
                role: 'presentation',
                cls: Ext.baseCSSPrefix + 'textmetrics'
            });
        measure.setVisibilityMode(1);
        me.measure = measure;
        if (bindTo) {
            me.bind(bindTo);
        }
        measure.position('absolute');
        measure.setLocalXY(-1000, -1000);
        measure.hide();
        if (fixedWidth) {
            measure.setWidth(fixedWidth);
        }
    },
    
    getSize: function(text) {
        var measure = this.measure,
            size;
        measure.setHtml(text);
        size = measure.getSize();
        measure.setHtml('');
        return size;
    },
    
    bind: function(el) {
        var me = this;
        me.el = Ext.get(el);
        me.measure.setStyle(me.el.getStyle([
            'font-size',
            'font-style',
            'font-weight',
            'font-family',
            'line-height',
            'text-transform',
            'letter-spacing',
            'word-break'
        ]));
    },
    
    setFixedWidth: function(width) {
        this.measure.setWidth(width);
    },
    
    getWidth: function(text) {
        this.measure.dom.style.width = 'auto';
        return this.getSize(text).width;
    },
    
    getHeight: function(text) {
        return this.getSize(text).height;
    },
    
    destroy: function() {
        var me = this;
        me.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.AbstractComponent', {
    extend: Ext.Widget
});


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.Panel', {
    extend: Ext.Container,
    xtype: 'panel',
    alternateClassName: 'Ext.panel.Panel',
    defaultBindProperty: 'title',
    isPanel: true,
    config: {
        baseCls: Ext.baseCSSPrefix + 'panel',
        
        border: false,
        
        bodyPadding: null,
        
        bodyBorder: null,
        header: null,
        icon: null,
        iconCls: null,
        title: null,
        tools: null
    },
    manageBorders: true,
    getElementConfig: function() {
        return {
            reference: 'element',
            classList: [
                'x-container',
                'x-unsized'
            ],
            children: [
                {
                    reference: 'innerElement',
                    className: 'x-inner'
                },
                {
                    reference: 'tipElement',
                    className: 'x-anchor',
                    hidden: true
                }
            ]
        };
    },
    
    addBodyCls: function(cls) {
        this.innerElement.addCls(cls);
        return this;
    },
    
    removeBodyCls: function(cls) {
        this.innerElement.removeCls(cls);
        return this;
    },
    applyBodyPadding: function(bodyPadding) {
        if (bodyPadding === true) {
            bodyPadding = 5;
        }
        if (bodyPadding) {
            bodyPadding = Ext.dom.Element.unitizeBox(bodyPadding);
        }
        return bodyPadding;
    },
    addTool: function(tool) {
        var header = this.ensureHeader(),
            
            items;
        if (header) {
            items = header.createTools(Ext.Array.from(tool), this);
            if (items && items.length) {
                items = header.add(items);
            }
        }
        return items;
    },
    applyHeader: function(newHeader, oldHeader) {
        var me = this,
            header = oldHeader;
        if (newHeader === false) {
            if (header) {
                me.remove(header);
                header = null;
            }
        } else if (newHeader) {
            if (header) {
                if (newHeader !== true) {
                    header.setConfig(newHeader);
                }
            } else {
                
                header = me.add(me.createHeader(newHeader));
            }
        }
        return header || null;
    },
    applyTools: function(tools) {
        var header = this.ensureHeader(),
            
            items;
        if (header) {
            
            header.clearTools();
            items = header.createTools(tools, this);
            if (items && items.length) {
                header.add(items);
            }
        }
    },
    
    createHeader: function(config) {
        var me = this,
            ret = {
                xtype: 'panelheader',
                docked: 'top',
                ui: me.getUi()
            },
            icon, title;
        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;
                }
            }
        }
        return ret;
    },
    updateBorder: function(border, oldBorder) {
        this.callParent([
            border,
            oldBorder
        ]);
        if (this.getBodyBorder() === null) {
            this.setBodyBorderEnabled(border !== false);
        }
    },
    updateBodyPadding: function(newBodyPadding) {
        this.innerElement.setStyle('padding', newBodyPadding);
    },
    updateBodyBorder: function(bodyBorder) {
        var border = (bodyBorder === null) ? this.getBorder() : bodyBorder;
        this.setBodyBorderEnabled(bodyBorder !== false);
    },
    updateIcon: function(icon) {
        var header = this.ensureHeader();
        
        if (header) {
            header.setIcon(icon);
        }
    },
    updateIconCls: function(icon) {
        var header = this.ensureHeader();
        
        if (header) {
            header.setIconCls(icon);
        }
    },
    updateTitle: function(title) {
        var header = this.ensureHeader();
        
        if (header) {
            header.setTitle(title);
        }
    },
    updateUi: function(ui, oldUi) {
        var me = this,
            suffix = 'x-panel-inner-',
            innerElement = me.innerElement,
            
            
            header = !me.isConfiguring && me.ensureHeader();
        if (oldUi) {
            innerElement.removeCls(suffix + oldUi);
        }
        if (ui) {
            innerElement.addCls(suffix + ui);
        }
        if (header) {
            me.getTitle();
            header.setUi(ui);
        }
        me.callParent([
            ui,
            oldUi
        ]);
    },
    alignTo: function(component, alignment) {
        var alignmentInfo = this.getAlignmentInfo(component, alignment);
        if (alignmentInfo.isAligned)  {
            return;
        }
        
        var tipElement = this.tipElement;
        tipElement.hide();
        if (this.currentTipPosition) {
            tipElement.removeCls('x-anchor-' + this.currentTipPosition);
        }
        this.callParent(arguments);
        var LineSegment = Ext.util.LineSegment,
            alignToElement = component.isComponent ? component.renderElement : component,
            element = this.renderElement,
            alignToBox = alignToElement.getBox(),
            box = element.getBox(),
            left = box.left,
            top = box.top,
            right = box.right,
            bottom = box.bottom,
            centerX = left + (box.width / 2),
            centerY = top + (box.height / 2),
            leftTopPoint = {
                x: left,
                y: top
            },
            rightTopPoint = {
                x: right,
                y: top
            },
            leftBottomPoint = {
                x: left,
                y: bottom
            },
            rightBottomPoint = {
                x: right,
                y: bottom
            },
            boxCenterPoint = {
                x: centerX,
                y: centerY
            },
            alignToCenterX = alignToBox.left + (alignToBox.width / 2),
            alignToCenterY = alignToBox.top + (alignToBox.height / 2),
            alignToBoxCenterPoint = {
                x: alignToCenterX,
                y: alignToCenterY
            },
            centerLineSegment = new LineSegment(boxCenterPoint, alignToBoxCenterPoint),
            offsetLeft = 0,
            offsetTop = 0,
            tipSize, tipWidth, tipHeight, tipPosition, tipX, tipY;
        tipElement.setVisibility(false);
        tipElement.show();
        tipSize = tipElement.getSize();
        tipWidth = tipSize.width;
        tipHeight = tipSize.height;
        if (centerLineSegment.intersects(new LineSegment(leftTopPoint, rightTopPoint))) {
            tipX = Math.min(Math.max(alignToCenterX, left + tipWidth), right - (tipWidth));
            tipY = top;
            offsetTop = tipHeight + 10;
            tipPosition = 'top';
        } else if (centerLineSegment.intersects(new LineSegment(leftTopPoint, leftBottomPoint))) {
            tipX = left;
            tipY = Math.min(Math.max(alignToCenterY + (tipWidth / 2), tipWidth * 1.6), bottom - (tipWidth / 2.2));
            offsetLeft = tipHeight + 10;
            tipPosition = 'left';
        } else if (centerLineSegment.intersects(new LineSegment(leftBottomPoint, rightBottomPoint))) {
            tipX = Math.min(Math.max(alignToCenterX, left + tipWidth), right - tipWidth);
            tipY = bottom;
            offsetTop = -tipHeight - 10;
            tipPosition = 'bottom';
        } else if (centerLineSegment.intersects(new LineSegment(rightTopPoint, rightBottomPoint))) {
            tipX = right;
            tipY = Math.max(Math.min(alignToCenterY - tipHeight, bottom - tipWidth * 1.3), tipWidth / 2);
            offsetLeft = -tipHeight - 10;
            tipPosition = 'right';
        }
        if (tipX || tipY) {
            this.currentTipPosition = tipPosition;
            tipElement.addCls('x-anchor-' + tipPosition);
            tipElement.setLeft(tipX - left);
            tipElement.setTop(tipY - top);
            tipElement.setVisibility(true);
            this.setLeft(this.getLeft() + offsetLeft);
            this.setTop(this.getTop() + offsetTop);
        }
    },
    privates: {
        ensureHeader: function() {
            var me = this,
                header;
            me.getViewModel();
            me.getItems();
            header = me.getHeader();
            if (!header && header !== false) {
                me.setHeader(true);
                header = me.getHeader();
            }
            return header;
        },
        setBodyBorderEnabled: function(enabled) {
            this.innerElement.setStyle('border-width', enabled ? '' : '0');
        }
    }
});


Ext.define('Ext.Button', {
    extend: Ext.Component,
    xtype: 'button',
    isButton: true,
    
    
    cachedConfig: {
        
        pressedCls: Ext.baseCSSPrefix + 'button-pressing',
        
        badgeCls: Ext.baseCSSPrefix + 'badge',
        
        hasBadgeCls: Ext.baseCSSPrefix + 'hasbadge',
        
        labelCls: Ext.baseCSSPrefix + 'button-label',
        
        iconCls: null,
        
        textAlign: null
    },
    config: {
        
        badgeText: null,
        
        text: null,
        
        icon: false,
        
        iconAlign: 'left',
        
        pressedDelay: 0,
        
        handler: null,
        
        scope: null,
        
        autoEvent: null,
        
        ui: null,
        
        
        baseCls: Ext.baseCSSPrefix + 'button'
    },
    defaultBindProperty: 'text',
    template: [
        {
            tag: 'span',
            reference: 'badgeElement',
            hidden: true
        },
        {
            tag: 'span',
            className: Ext.baseCSSPrefix + 'button-icon',
            reference: 'iconElement'
        },
        {
            tag: 'span',
            reference: 'textElement',
            hidden: true
        }
    ],
    initialize: function() {
        this.callParent();
        this.element.on({
            scope: this,
            tap: 'onTap',
            touchstart: 'onPress',
            touchend: 'onRelease'
        });
    },
    
    updateBadgeText: function(badgeText) {
        var element = this.element,
            badgeElement = this.badgeElement;
        if (badgeText) {
            badgeElement.show();
            badgeElement.setText(badgeText);
        } else {
            badgeElement.hide();
        }
        element[(badgeText) ? 'addCls' : 'removeCls'](this.getHasBadgeCls());
    },
    
    updateText: function(text) {
        var textElement = this.textElement;
        if (textElement) {
            if (text) {
                textElement.show();
                textElement.setHtml(text);
            } else {
                textElement.hide();
            }
            this.refreshIconAlign();
        }
    },
    
    updateHtml: function(html) {
        var textElement = this.textElement;
        if (html) {
            textElement.show();
            textElement.setHtml(html);
        } else {
            textElement.hide();
        }
    },
    
    updateBadgeCls: function(badgeCls, oldBadgeCls) {
        this.badgeElement.replaceCls(oldBadgeCls, badgeCls);
    },
    
    updateHasBadgeCls: function(hasBadgeCls, oldHasBadgeCls) {
        var element = this.element;
        if (element.hasCls(oldHasBadgeCls)) {
            element.replaceCls(oldHasBadgeCls, hasBadgeCls);
        }
    },
    
    updateLabelCls: function(labelCls, oldLabelCls) {
        this.textElement.replaceCls(oldLabelCls, labelCls);
    },
    
    updatePressedCls: function(pressedCls, oldPressedCls) {
        var element = this.element;
        if (element.hasCls(oldPressedCls)) {
            element.replaceCls(oldPressedCls, pressedCls);
        }
    },
    
    updateIcon: function(icon) {
        var me = this,
            element = me.iconElement;
        if (icon) {
            me.showIconElement();
            element.setStyle('background-image', 'url(' + icon + ')');
            me.refreshIconAlign();
        } else {
            element.setStyle('background-image', '');
            if (!me.getIconCls()) {
                me.hideIconElement();
            }
        }
    },
    
    updateIconCls: function(iconCls, oldIconCls) {
        var me = this,
            element = me.iconElement;
        if (iconCls) {
            me.showIconElement();
            element.replaceCls(oldIconCls, iconCls);
            me.refreshIconAlign();
        } else {
            element.removeCls(oldIconCls);
            if (!me.getIcon()) {
                me.hideIconElement();
            }
        }
    },
    
    updateIconAlign: function(alignment, oldAlignment) {
        var element = this.element,
            baseCls = Ext.baseCSSPrefix + 'iconalign-';
        if (!this.getText()) {
            alignment = 'center';
        }
        element.removeCls(baseCls + 'center');
        element.removeCls(baseCls + oldAlignment);
        if (this.getIcon() || this.getIconCls()) {
            element.addCls(baseCls + alignment);
        }
    },
    _textAlignCls: {
        left: Ext.baseCSSPrefix + 'text-align-left',
        right: Ext.baseCSSPrefix + 'text-align-right',
        center: ''
    },
    updateTextAlign: function(textAlign, oldValue) {
        var textAlignClasses = this._textAlignCls,
            add = textAlignClasses[textAlign || 'center'],
            remove = textAlignClasses[oldValue || 'center'];
        this.replaceCls(remove, add);
    },
    refreshIconAlign: function() {
        this.updateIconAlign(this.getIconAlign());
    },
    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);
    },
    
    hideIconElement: function() {
        var el = this.iconElement;
        el.removeCls(Ext.baseCSSPrefix + 'shown');
        el.addCls(Ext.baseCSSPrefix + 'hidden');
        this.element.addCls(Ext.baseCSSPrefix + 'button-no-icon');
    },
    
    showIconElement: function() {
        var el = this.iconElement;
        el.addCls(Ext.baseCSSPrefix + 'shown');
        el.removeCls(Ext.baseCSSPrefix + 'hidden');
        this.element.removeCls(Ext.baseCSSPrefix + 'button-no-icon');
    },
    
    applyUi: function(config) {
        if (config && Ext.isString(config)) {
            var array = config.split('-');
            if (array && (array[1] == "back" || array[1] == "forward")) {
                return array;
            }
        }
        return config;
    },
    getUi: function() {
        
        var ui = this._ui;
        if (Ext.isArray(ui)) {
            return ui.join('-');
        }
        return ui;
    },
    applyPressedDelay: function(delay) {
        if (Ext.isNumber(delay)) {
            return delay;
        }
        return (delay) ? 100 : 0;
    },
    
    onPress: function() {
        var me = this,
            element = me.element,
            pressedDelay = me.getPressedDelay(),
            pressedCls = me.getPressedCls();
        if (!me.getDisabled()) {
            if (pressedDelay > 0) {
                me.pressedTimeout = Ext.defer(function() {
                    delete me.pressedTimeout;
                    if (element) {
                        element.addCls(pressedCls);
                    }
                }, pressedDelay);
            } else {
                element.addCls(pressedCls);
            }
        }
    },
    
    onRelease: function(e) {
        this.fireAction('release', [
            this,
            e
        ], 'doRelease');
    },
    
    doRelease: function(me, e) {
        if (!me.getDisabled()) {
            if (me.hasOwnProperty('pressedTimeout')) {
                clearTimeout(me.pressedTimeout);
                delete me.pressedTimeout;
            } else {
                me.element.removeCls(me.getPressedCls());
            }
        }
    },
    
    onTap: function(e) {
        if (this.getDisabled()) {
            return false;
        }
        this.fireAction('tap', [
            this,
            e
        ], 'doTap');
    },
    
    doTap: function(me, e) {
        var handler = me.getHandler();
        if (!handler) {
            return;
        }
        
        
        if (e && e.preventDefault) {
            e.preventDefault();
        }
        Ext.callback(handler, me.getScope(), [
            me,
            e
        ], 0, me);
    }
});


Ext.define('Ext.Sheet', {
    extend: Ext.Panel,
    xtype: 'sheet',
    config: {
        
        baseCls: Ext.baseCSSPrefix + 'sheet',
        
        modal: true,
        
        centered: true,
        
        stretchX: null,
        
        stretchY: null,
        
        enter: 'bottom',
        
        exit: 'bottom',
        
        showAnimation: {
            type: 'slideIn',
            duration: 250,
            easing: 'ease-out'
        },
        
        hideAnimation: {
            type: 'slideOut',
            duration: 250,
            easing: 'ease-in'
        },
        
        border: null
    },
    manageBorders: false,
    isInputRegex: /^(input|textarea|select|a)$/i,
    beforeInitialize: function() {
        var me = this;
        
        
        if (Ext.os.is.iOS) {
            this.element.dom.addEventListener('touchstart', function(e) {
                if (!me.isInputRegex.test(e.target.tagName)) {
                    e.preventDefault();
                }
            }, true);
        }
    },
    applyHideAnimation: function(config) {
        var exit = this.getExit(),
            direction = exit;
        if (exit === null) {
            return null;
        }
        if (config === true) {
            config = {
                type: 'slideOut'
            };
        }
        if (Ext.isString(config)) {
            config = {
                type: config
            };
        }
        var anim = Ext.factory(config, Ext.fx.Animation);
        if (anim) {
            if (exit == 'bottom') {
                direction = 'down';
            }
            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'
            };
        }
        if (Ext.isString(config)) {
            config = {
                type: config
            };
        }
        var anim = Ext.factory(config, Ext.fx.Animation);
        if (anim) {
            if (enter == 'bottom') {
                direction = 'down';
            }
            if (enter == 'top') {
                direction = 'up';
            }
            anim.setBefore({
                display: null
            });
            anim.setReverse(true);
            anim.setDirection(direction);
        }
        return anim;
    },
    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,
    alias: 'widget.actionsheet',
    config: {
        
        baseCls: Ext.baseCSSPrefix + 'sheet-action',
        
        left: 0,
        
        right: 0,
        
        bottom: 0,
        
        centered: false,
        
        height: 'auto',
        
        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';
        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;
            
            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;
        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;
            }
            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
        })
    };
});


Ext.define('Ext.Media', {
    extend: Ext.Component,
    xtype: 'media',
    
    
    
    
    
    
    
    config: {
        
        url: '',
        
        enableControls: Ext.os.is.Android ? false : true,
        
        autoResume: false,
        
        autoPause: true,
        
        preload: true,
        
        loop: false,
        
        media: null,
        
        volume: 1,
        
        muted: false
    },
    constructor: function() {
        this.mediaEvents = {};
        this.callParent(arguments);
    },
    initialize: function() {
        var me = this;
        me.callParent();
        me.on({
            scope: me,
            show: me.onActivate,
            hide: me.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.media.dom.controls = enableControls ? 'controls' : false;
    },
    
    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;
    },
    destroy: function() {
        var me = this,
            dom = me.media.dom,
            mediaEvents = me.mediaEvents;
        Ext.Object.each(mediaEvents, function(event, fn) {
            dom.removeEventListener(event, fn);
        });
        me.callParent();
    }
});


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.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) {
            clearInterval(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) {
            clearInterval(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.Map', {
    extend: Ext.Container,
    xtype: 'map',
    isMap: true,
    config: {
        
        
        
        
        
        baseCls: Ext.baseCSSPrefix + 'map',
        
        useCurrentLocation: false,
        
        map: null,
        
        geo: null,
        
        mapOptions: {},
        
        mapListeners: null
    },
    constructor: function() {
        this.callParent(arguments);
        
        if (!(window.google || {}).maps) {
            this.setHtml('Google Maps API is required');
        }
    },
    initialize: function() {
        this.callParent();
        this.initMap();
        this.on({
            painted: 'doResize',
            scope: this
        });
        this.innerElement.on('touchstart', 'onTouchStart', this);
    },
    initMap: function() {
        var map = this.getMap();
        if (!map) {
            var gm = (window.google || {}).maps;
            if (!gm)  {
                return null;
            }
            
            var element = this.mapContainer,
                mapOptions = this.getMapOptions(),
                event = gm.event,
                me = this;
            
            if (element.dom.firstChild) {
                Ext.fly(element.dom.firstChild).destroy();
            }
            if (Ext.os.is.iPad) {
                Ext.merge({
                    navigationControlOptions: {
                        style: gm.NavigationControlStyle.ZOOM_PAN
                    }
                }, mapOptions);
            }
            mapOptions.mapTypeId = mapOptions.mapTypeId || gm.MapTypeId.ROADMAP;
            mapOptions.center = mapOptions.center || new gm.LatLng(37.381592, -122.135672);
            
            if (mapOptions.center && mapOptions.center.latitude && !Ext.isFunction(mapOptions.center.lat)) {
                mapOptions.center = new gm.LatLng(mapOptions.center.latitude, mapOptions.center.longitude);
            }
            mapOptions.zoom = mapOptions.zoom || 12;
            map = new gm.Map(element.dom, mapOptions);
            this.setMap(map);
            event.addListener(map, 'zoom_changed', Ext.bind(me.onZoomChange, me));
            event.addListener(map, 'maptypeid_changed', Ext.bind(me.onTypeChange, me));
            event.addListener(map, 'center_changed', Ext.bind(me.onCenterChange, me));
            event.addListenerOnce(map, 'tilesloaded', Ext.bind(me.onTilesLoaded, me));
            this.addMapListeners();
        }
        return this.getMap();
    },
    
    renderMap: function() {
        this.initMap();
    },
    getElementConfig: function() {
        return {
            reference: 'element',
            className: 'x-container',
            children: [
                {
                    reference: 'innerElement',
                    className: 'x-inner',
                    children: [
                        {
                            reference: 'mapContainer',
                            className: Ext.baseCSSPrefix + 'map-container'
                        }
                    ]
                }
            ]
        };
    },
    onTouchStart: function(e) {
        e.makeUnpreventable();
    },
    applyMapOptions: function(options) {
        return Ext.merge({}, this.options, options);
    },
    updateMapOptions: function(newOptions) {
        var gm = (window.google || {}).maps,
            map = this.getMap();
        if (gm && map) {
            map.setOptions(newOptions);
        }
    },
    doMapCenter: function() {
        this.setMapCenter(this.getMapOptions().center);
    },
    getMapOptions: function() {
        return Ext.merge({}, this.options || this.getInitialConfig('mapOptions'));
    },
    updateUseCurrentLocation: function(useCurrentLocation) {
        this.setGeo(useCurrentLocation);
        if (!useCurrentLocation) {
            this.setMapCenter();
        }
    },
    applyGeo: function(config) {
        return Ext.factory(config, Ext.util.Geolocation, this.getGeo());
    },
    updateGeo: function(newGeo, oldGeo) {
        var events = {
                locationupdate: 'onGeoUpdate',
                locationerror: 'onGeoError',
                scope: this
            };
        if (oldGeo) {
            oldGeo.un(events);
        }
        if (newGeo) {
            newGeo.on(events);
            newGeo.updateLocation();
        }
    },
    doResize: function() {
        var gm = (window.google || {}).maps,
            map = this.getMap();
        if (gm && map) {
            gm.event.trigger(map, "resize");
        }
    },
    
    onTilesLoaded: function() {
        this.fireEvent('maprender', this, this.getMap());
    },
    
    addMapListeners: function() {
        var gm = (window.google || {}).maps,
            map = this.getMap(),
            mapListeners = this.getMapListeners();
        if (gm) {
            var event = gm.event,
                me = this,
                listener, scope, fn, callbackFn, handle;
            if (Ext.isSimpleObject(mapListeners)) {
                for (var eventType in mapListeners) {
                    listener = mapListeners[eventType];
                    if (Ext.isSimpleObject(listener)) {
                        scope = listener.scope;
                        fn = listener.fn;
                    } else if (Ext.isFunction(listener)) {
                        scope = null;
                        fn = listener;
                    }
                    if (fn) {
                        callbackFn = function() {
                            this.fn.apply(this.scope, [
                                me
                            ]);
                            if (this.handle) {
                                event.removeListener(this.handle);
                                delete this.handle;
                                delete this.fn;
                                delete this.scope;
                            }
                        };
                        handle = event.addListener(map, eventType, Ext.bind(callbackFn, callbackFn));
                        callbackFn.fn = fn;
                        callbackFn.scope = scope;
                        if (listener.single === true)  {
                            callbackFn.handle = handle;
                        }
                        
                    }
                }
            }
        }
    },
    
    onGeoUpdate: function(geo) {
        if (geo) {
            this.setMapCenter(new google.maps.LatLng(geo.getLatitude(), geo.getLongitude()));
        }
    },
    
    onGeoError: Ext.emptyFn,
    
    setMapCenter: function(coordinates) {
        var me = this,
            map = me.getMap(),
            mapOptions = me.getMapOptions(),
            gm = (window.google || {}).maps;
        if (gm) {
            if (!coordinates) {
                if (map && map.getCenter) {
                    coordinates = map.getCenter();
                } else if (mapOptions.hasOwnProperty('center')) {
                    coordinates = mapOptions.center;
                } else {
                    coordinates = new gm.LatLng(37.381592, -122.135672);
                }
            }
            
            if (coordinates && !(coordinates instanceof gm.LatLng) && 'longitude' in coordinates) {
                coordinates = new gm.LatLng(coordinates.latitude, coordinates.longitude);
            }
            if (!map) {
                mapOptions.center = mapOptions.center || coordinates;
                me.renderMap();
                map = me.getMap();
            }
            if (map && coordinates instanceof gm.LatLng) {
                map.panTo(coordinates);
            } else {
                this.options = Ext.apply(this.getMapOptions(), {
                    center: coordinates
                });
            }
        }
    },
    
    onZoomChange: function() {
        var mapOptions = this.getMapOptions(),
            map = this.getMap(),
            zoom;
        zoom = (map && map.getZoom) ? map.getZoom() : mapOptions.zoom || 10;
        this.options = Ext.apply(mapOptions, {
            zoom: zoom
        });
        this.fireEvent('zoomchange', this, map, zoom);
    },
    
    onTypeChange: function() {
        var mapOptions = this.getMapOptions(),
            map = this.getMap(),
            mapTypeId;
        mapTypeId = (map && map.getMapTypeId) ? map.getMapTypeId() : mapOptions.mapTypeId;
        this.options = Ext.apply(mapOptions, {
            mapTypeId: mapTypeId
        });
        this.fireEvent('typechange', this, map, mapTypeId);
    },
    
    onCenterChange: function() {
        var mapOptions = this.getMapOptions(),
            map = this.getMap(),
            center;
        center = (map && map.getCenter) ? map.getCenter() : mapOptions.center;
        this.options = Ext.apply(mapOptions, {
            center: center
        });
        this.fireEvent('centerchange', this, map, center);
    },
    
    destroy: function() {
        Ext.destroy(this.getGeo());
        var map = this.getMap();
        if (map && (window.google || {}).maps) {
            google.maps.event.clearInstanceListeners(map);
        }
        this.callParent();
    }
});


Ext.define('Ext.BingMap', {
    extend: Ext.Map,
    xtype: 'bingmap',
    
    initMap: function() {
        var map = this.getMap();
        if (!map) {
            var me = this,
                element = me.mapContainer,
                mapOptions = me.getMapOptions(),
                event;
            var MM = Microsoft.Maps;
            var key = "AokX-S2lieXTaXG8pvEw3i2AKYuStBMK8RsUu6BDJ6hrL5AYv0IfQqM9zc-BAA-v";
            
            mapOptions = Ext.merge({
                credentials: key,
                mapTypeId: "r",
                zoom: 12
            }, mapOptions);
            
            
            if (!mapOptions.center) {
                mapOptions.center = new MM.Location(37.381592, -122.135672);
            }
            
            if (element.dom.firstChild) {
                Ext.fly(element.dom.firstChild).destroy();
            }
            MM.loadModule('Microsoft.Maps.Overlays.Style', {
                callback: function() {
                    me.setMap(new MM.Map(element.dom, mapOptions));
                    if (mapOptions.callback) {
                        mapOptions.callback();
                    }
                }
            });
            map = me.getMap();
        }
        
        
        
        
        
        
        me.fireEvent('maprender', me, map);
    },
    setMapCenter: function(coordinates) {
        var me = this,
            map = me.getMap(),
            MM = Microsoft.Maps;
        if (!me.isPainted()) {
            me.un('painted', 'setMapCenter', this);
            me.on('painted', 'setMapCenter', this, {
                delay: 150,
                single: true,
                args: [
                    coordinates
                ]
            });
            return;
        }
        coordinates = coordinates || new MM.Location(37.381592, -122.135672);
        if (coordinates && !(coordinates instanceof MM.Location) && 'longitude' in coordinates) {
            coordinates = new MM.Location(coordinates.latitude, coordinates.longitude);
        }
        if (!map) {
            me.initMap();
            map = me.getMap();
        }
        if (map && coordinates instanceof MM.Location) {
            map.updateMapPosition(coordinates);
        } else {
            this.options = Ext.apply(this.getMapOptions(), {
                center: coordinates
            });
        }
    }
}, function() {});


Ext.define('Ext.Decorator', {
    extend: Ext.Component,
    isDecorator: true,
    config: {
        
        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);
            }
        }
    },
    
    applyComponent: function(config) {
        return Ext.factory(config, Ext.Component);
    },
    
    updateComponent: function(newComponent, oldComponent) {
        var me = this;
        if (oldComponent) {
            if (me.isRendered() && oldComponent.setRendered(false)) {
                oldComponent.fireEventedAction('renderedchange', [
                    me,
                    oldComponent,
                    false
                ], me.doUnsetComponent, me, false);
            } else {
                me.doUnsetComponent(oldComponent);
            }
        }
        if (newComponent) {
            if (me.isRendered() && newComponent.setRendered(true)) {
                newComponent.fireEventedAction('renderedchange', [
                    me,
                    newComponent,
                    true
                ], me.doSetComponent, me, false);
            } else {
                me.doSetComponent(newComponent);
            }
        }
    },
    
    doUnsetComponent: function(component) {
        var dom = component.renderElement.dom;
        if (dom) {
            component.setLayoutSizeFlags(0);
            this.innerElement.dom.removeChild(dom);
        }
    },
    
    doSetComponent: function(component) {
        var dom = component.renderElement.dom;
        if (dom) {
            component.setLayoutSizeFlags(this.getSizeFlags());
            this.innerElement.dom.appendChild(dom);
        }
    },
    
    setRendered: function(rendered) {
        var component;
        if (this.callParent(arguments)) {
            component = this.getComponent();
            if (component) {
                component.setRendered(rendered);
            }
            return true;
        }
        return false;
    },
    
    setDisabled: function(disabled) {
        
        this.callParent(arguments);
        
        
        
        
        this.getComponent().setDisabled(disabled);
    },
    destroy: function() {
        Ext.destroy(this.getComponent());
        this.callParent();
    }
});


Ext.define('Ext.Img', {
    extend: Ext.Component,
    xtype: [
        'image',
        'img'
    ],
    alternateClassName: 'Ext.Image',
    
    
    
    config: {
        
        src: null,
        
        baseCls: Ext.baseCSSPrefix + 'img',
        
        imageCls: Ext.baseCSSPrefix + 'img-image',
        
        backgroundCls: Ext.baseCSSPrefix + 'img-background',
        
        mode: 'background'
    },
    beforeInitialize: function() {
        var me = this;
        me.onLoad = Ext.Function.bind(me.onLoad, me);
        me.onError = Ext.Function.bind(me.onError, me);
    },
    initialize: function() {
        var me = this;
        me.callParent();
        me.relayEvents(me.renderElement, '*');
        me.element.on({
            tap: 'onTap',
            scope: me
        });
    },
    hide: function() {
        this.callParent(arguments);
        this.hiddenSrc = this.hiddenSrc || this.getSrc();
        this.setSrc(null);
    },
    show: 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);
    },
    onAfterRender: function() {
        this.updateSrc(this.getSrc());
    },
    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);
    },
    destroy: 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',
    config: {
        baseCls: Ext.baseCSSPrefix + 'label'
    }
});



Ext.define('Ext.Menu', {
    extend: Ext.Sheet,
    xtype: 'menu',
    config: {
        
        baseCls: Ext.baseCSSPrefix + 'menu',
        
        left: 0,
        
        right: 0,
        
        bottom: 0,
        
        height: 'auto',
        
        width: 'auto',
        
        defaultType: 'button',
        
        showAnimation: null,
        
        hideAnimation: null,
        
        centered: false,
        
        modal: true,
        
        hidden: true,
        
        hideOnMaskTap: true,
        
        translatable: {
            translationMethod: null
        }
    },
    constructor: function() {
        this.config.translatable.translationMethod = 'csstransform';
        this.callParent(arguments);
    },
    updateUi: function(newUi, oldUi) {
        this.callParent(arguments);
        if (newUi != oldUi && Ext.theme.is.Blackberry) {
            if (newUi == 'context') {
                this.innerElement.swapCls('x-vertical', 'x-horizontal');
            } else if (newUi == 'application') {
                this.innerElement.swapCls('x-horizontal', 'x-vertical');
            }
        }
    },
    updateHideOnMaskTap: function(hide) {
        var mask = this.getModal();
        if (mask) {
            mask[hide ? 'on' : 'un'].call(mask, 'tap', function() {
                Ext.Viewport.hideMenu(this.$side);
            }, this);
        }
    },
    
    updateHidden: function() {
        if (this.initialized) {
            this.callParent(arguments);
        }
    }
});


Ext.define('Ext.Title', {
    extend: Ext.Component,
    xtype: 'title',
    config: {
        
        baseCls: 'x-title',
        
        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: {
        
        baseCls: Ext.baseCSSPrefix + 'toolbar',
        
        title: null,
        
        defaultType: 'button',
        
        defaultButtonUI: null,
        
        
        minHeight: null,
        
        layout: {
            type: 'hbox',
            align: 'center'
        }
    },
    hasCSSMinHeight: true,
    constructor: function(config) {
        config = config || {};
        if (config.docked == "left" || config.docked == "right") {
            config.layout = {
                type: 'vbox',
                align: 'stretch'
            };
        }
        this.callParent([
            config
        ]);
    },
    
    applyTitle: function(title) {
        if (typeof title == 'string') {
            title = {
                title: title,
                centered: Ext.theme.is.Tizen ? false : 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.field.Input', {
    extend: Ext.Component,
    xtype: 'input',
    
    
    
    
    
    
    
    
    
    tag: 'input',
    cachedConfig: {
        
        cls: Ext.baseCSSPrefix + 'form-field',
        
        focusCls: Ext.baseCSSPrefix + 'field-focus',
        
        maskCls: Ext.baseCSSPrefix + 'field-mask',
        
        useMask: 'auto',
        
        type: 'text',
        
        checked: false
    },
    config: {
        
        baseCls: Ext.baseCSSPrefix + 'field-input',
        
        name: null,
        
        value: null,
        
        isFocused: false,
        
        tabIndex: null,
        
        placeHolder: null,
        
        minValue: null,
        
        maxValue: null,
        
        stepValue: null,
        
        maxLength: null,
        
        autoComplete: null,
        
        autoCapitalize: null,
        
        autoCorrect: null,
        
        readOnly: null,
        
        maxRows: null,
        
        pattern: null,
        
        
        startValue: false,
        
        fastFocus: false
    },
    
    
    getTemplate: function() {
        var items = [
                {
                    reference: 'input',
                    tag: this.tag
                },
                {
                    reference: 'mask',
                    classList: [
                        this.config.maskCls
                    ]
                },
                {
                    reference: 'clearIcon',
                    cls: 'x-clear-icon'
                }
            ];
        return items;
    },
    initElement: function() {
        var me = this;
        me.callParent();
        me.input.on({
            scope: me,
            keyup: 'onKeyUp',
            keydown: 'onKeyDown',
            focus: 'onFocus',
            blur: 'onBlur',
            input: 'onInput',
            paste: 'onPaste',
            tap: 'onInputTap'
        });
        
        
        
        if (Ext.browser.is.AndroidStock) {
            me.input.dom.addEventListener("mousedown", function(e) {
                if (document.activeElement != e.target) {
                    e.preventDefault();
                }
            });
            me.input.dom.addEventListener("touchend", function() {
                me.focus();
            });
        }
        me.mask.on({
            scope: me,
            tap: 'onMaskTap'
        });
        if (me.clearIcon) {
            me.clearIcon.on({
                tap: 'onClearIconTap',
                touchstart: 'onClearIconPress',
                touchend: 'onClearIconRelease',
                scope: me
            });
        }
        
        if (Ext.browser.is.ie && Ext.browser.version.major >= 10) {
            me.input.on({
                scope: me,
                keypress: 'onKeyPress'
            });
        }
    },
    updateFastFocus: function(newValue) {
        
        if (newValue) {
            if (this.getFastFocus() && Ext.os.is.iOS) {
                this.input.on({
                    scope: this,
                    touchstart: "onTouchStart"
                });
            }
        } else {
            this.input.un({
                scope: this,
                touchstart: "onTouchStart"
            });
        }
    },
    
    useManualMaxLength: function() {
        return Boolean((Ext.os.is.Android && !Ext.browser.is.Chrome));
    },
    applyUseMask: function(useMask) {
        if (useMask === 'auto') {
            useMask = Ext.os.is.iOS && Ext.os.version.lt('5');
        }
        return Boolean(useMask);
    },
    
    updateUseMask: function(newUseMask) {
        this.mask[newUseMask ? 'show' : 'hide']();
    },
    updatePattern: function(pattern) {
        this.updateFieldAttribute('pattern', pattern);
    },
    
    updateFieldAttribute: function(attribute, newValue) {
        var input = this.input;
        if (!Ext.isEmpty(newValue, true)) {
            input.dom.setAttribute(attribute, newValue);
        } else {
            input.dom.removeAttribute(attribute);
        }
    },
    
    updateCls: function(newCls, oldCls) {
        this.input.addCls(Ext.baseCSSPrefix + 'input-el');
        this.input.replaceCls(oldCls, newCls);
    },
    
    updateType: function(newType, oldType) {
        var prefix = Ext.baseCSSPrefix + 'input-';
        this.input.replaceCls(prefix + oldType, prefix + newType);
        this.updateFieldAttribute('type', newType);
    },
    
    updateName: function(newName) {
        this.updateFieldAttribute('name', newName);
    },
    
    getValue: function() {
        var input = this.input;
        if (input) {
            this._value = input.dom.value;
        }
        return this._value;
    },
    
    applyValue: function(value) {
        return (Ext.isEmpty(value)) ? '' : value;
    },
    
    updateValue: function(newValue) {
        var input = this.input;
        
        
        
        if (input && input.dom.value !== newValue) {
            input.dom.value = newValue;
        }
    },
    setValue: function(newValue) {
        var oldValue = this._value;
        this.updateValue(this.applyValue(newValue));
        newValue = this.getValue();
        if (String(newValue) != String(oldValue) && this.initialized) {
            this.onChange(this, newValue, oldValue);
        }
        return this;
    },
    
    
    applyTabIndex: function(tabIndex) {
        if (tabIndex !== null && typeof tabIndex != 'number') {
            throw new Error("Ext.field.Field: [applyTabIndex] trying to pass a value which is not a number");
        }
        return tabIndex;
    },
    
    
    updateTabIndex: function(newTabIndex) {
        this.updateFieldAttribute('tabIndex', newTabIndex);
    },
    
    testAutoFn: function(value) {
        return [
            true,
            'on'
        ].indexOf(value) !== -1;
    },
    
    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) {
        if (!this.useManualMaxLength()) {
            this.updateFieldAttribute('maxlength', newMaxLength);
        }
    },
    
    updatePlaceHolder: function(newPlaceHolder) {
        this.updateFieldAttribute('placeholder', newPlaceHolder);
    },
    
    applyAutoComplete: function(autoComplete) {
        return this.testAutoFn(autoComplete);
    },
    
    updateAutoComplete: function(newAutoComplete) {
        var value = newAutoComplete ? 'on' : 'off';
        this.updateFieldAttribute('autocomplete', value);
    },
    
    applyAutoCapitalize: function(autoCapitalize) {
        return this.testAutoFn(autoCapitalize);
    },
    
    updateAutoCapitalize: function(newAutoCapitalize) {
        var value = newAutoCapitalize ? 'on' : 'off';
        this.updateFieldAttribute('autocapitalize', value);
    },
    
    applyAutoCorrect: function(autoCorrect) {
        return this.testAutoFn(autoCorrect);
    },
    
    updateAutoCorrect: function(newAutoCorrect) {
        var value = newAutoCorrect ? 'on' : 'off';
        this.updateFieldAttribute('autocorrect', value);
    },
    
    updateMinValue: function(newMinValue) {
        this.updateFieldAttribute('min', newMinValue);
    },
    
    updateMaxValue: function(newMaxValue) {
        this.updateFieldAttribute('max', newMaxValue);
    },
    
    updateStepValue: function(newStepValue) {
        this.updateFieldAttribute('step', newStepValue);
    },
    
    checkedRe: /^(true|1|on)/i,
    
    getChecked: function() {
        var el = this.input,
            checked;
        if (el) {
            checked = el.dom.checked;
            this._checked = checked;
        }
        return checked;
    },
    
    applyChecked: function(checked) {
        return !!this.checkedRe.test(String(checked));
    },
    setChecked: function(newChecked) {
        this.updateChecked(this.applyChecked(newChecked));
        this._checked = newChecked;
    },
    
    updateChecked: function(newChecked) {
        this.input.dom.checked = newChecked;
    },
    
    updateReadOnly: function(readOnly) {
        this.updateFieldAttribute('readonly', readOnly ? true : null);
    },
    
    
    applyMaxRows: function(maxRows) {
        if (maxRows !== null && typeof maxRows !== 'number') {
            throw new Error("Ext.field.Input: [applyMaxRows] trying to pass a value which is not a number");
        }
        return maxRows;
    },
    
    updateMaxRows: function(newRows) {
        this.updateFieldAttribute('rows', newRows);
    },
    updateDisabled: function(disabled) {
        this.callParent(arguments);
        if (Ext.browser.is.Safari && !Ext.os.is.BlackBerry) {
            this.input.dom.tabIndex = (disabled) ? -1 : 0;
        }
        this.input.dom.disabled = (Ext.browser.is.Safari && !Ext.os.is.BlackBerry) ? false : disabled;
        if (!disabled) {
            this.blur();
        }
    },
    
    isDirty: function() {
        if (this.getDisabled()) {
            return false;
        }
        return String(this.getValue()) !== String(this.originalValue);
    },
    
    reset: function() {
        this.setValue(this.originalValue);
    },
    
    onInputTap: function(e) {
        this.fireAction('inputtap', [
            this,
            e
        ], 'doInputTap');
    },
    
    doInputTap: function(me, e) {
        if (me.getDisabled()) {
            return false;
        }
        
        if (this.getFastFocus() && Ext.os.is.iOS) {
            me.focus();
        }
    },
    
    onMaskTap: function(e) {
        this.fireAction('masktap', [
            this,
            e
        ], 'doMaskTap');
    },
    
    doMaskTap: function(me, e) {
        if (me.getDisabled()) {
            return false;
        }
        me.focus();
    },
    
    showMask: function() {
        if (this.getUseMask()) {
            this.mask.setStyle('display', 'block');
        }
    },
    
    hideMask: function() {
        if (this.getUseMask()) {
            this.mask.setStyle('display', 'none');
        }
    },
    
    focus: function() {
        var me = this,
            el = me.input;
        if (el && el.dom.focus) {
            el.dom.focus();
        }
        return me;
    },
    
    blur: function() {
        var me = this,
            el = this.input;
        if (el && el.dom.blur) {
            el.dom.blur();
        }
        return me;
    },
    
    select: function() {
        var me = this,
            el = me.input;
        if (el && el.dom.setSelectionRange) {
            el.dom.setSelectionRange(0, 9999);
        }
        return me;
    },
    onFocus: function(e) {
        this.fireAction('focus', [
            e
        ], 'doFocus');
    },
    
    doFocus: function(e) {
        var me = this;
        me.hideMask();
        if (!me.getIsFocused()) {
            me.setStartValue(me.getValue());
        }
        me.setIsFocused(true);
    },
    onTouchStart: function(e) {
        
        if (document.activeElement != e.target) {
            e.preventDefault();
        }
    },
    onBlur: function(e) {
        this.fireAction('blur', [
            e
        ], 'doBlur');
    },
    
    doBlur: function(e) {
        var me = this,
            value = me.getValue(),
            startValue = me.getStartValue();
        me.showMask();
        me.setIsFocused(false);
        if (String(value) != String(startValue)) {
            me.onChange(me, value, startValue);
        }
    },
    
    onClearIconTap: function(e) {
        this.fireEvent('clearicontap', this, e);
        
        
        if (Ext.os.is.Android) {
            this.focus();
        }
    },
    onClearIconPress: function() {
        this.clearIcon.addCls(Ext.baseCSSPrefix + 'pressing');
    },
    onClearIconRelease: function() {
        this.clearIcon.removeCls(Ext.baseCSSPrefix + 'pressing');
    },
    onClick: function(e) {
        this.fireEvent('click', e);
    },
    onChange: function(me, value, startValue) {
        if (this.useManualMaxLength()) {
            this.trimValueToMaxLength();
        }
        this.fireEvent('change', me, value, startValue);
    },
    onPaste: function(e) {
        if (this.useManualMaxLength()) {
            this.trimValueToMaxLength();
        }
        this.fireEvent('paste', e);
    },
    onKeyUp: function(e) {
        if (this.useManualMaxLength()) {
            this.trimValueToMaxLength();
        }
        this.fireEvent('keyup', e);
    },
    onKeyDown: function() {
        
        
        this.ignoreInput = true;
    },
    onInput: function(e) {
        var me = this;
        me.fireEvent('input', me, me.input.dom.value);
        
        if (me.ignoreInput) {
            me.ignoreInput = false;
            return;
        }
        
        
        Ext.defer(function() {
            if (!me.ignoreInput) {
                me.fireEvent('keyup', e);
                me.ignoreInput = false;
            }
        }, 10);
    },
    
    onKeyPress: function(e) {
        if (e.browserEvent.keyCode == 13) {
            this.fireEvent('keyup', e);
        }
    },
    onMouseDown: function(e) {
        this.fireEvent('mousedown', e);
    },
    trimValueToMaxLength: function() {
        var maxLength = this.getMaxLength();
        if (maxLength) {
            var value = this.getValue();
            if (value.length > this.getMaxLength()) {
                this.setValue(value.slice(0, maxLength));
            }
        }
    }
});


Ext.define('Ext.field.Field', {
    extend: Ext.Decorator,
    alternateClassName: 'Ext.form.Field',
    xtype: 'field',
    
    isField: true,
    
    isFormField: true,
    config: {
        
        baseCls: Ext.baseCSSPrefix + 'field',
        
        label: null,
        
        labelAlign: 'left',
        
        labelWidth: '30%',
        
        labelWrap: false,
        
        clearIcon: null,
        
        required: false,
        
        
        inputType: null,
        
        name: null,
        
        value: null,
        
        tabIndex: null
    },
    
    
    cachedConfig: {
        
        labelCls: null,
        
        requiredCls: Ext.baseCSSPrefix + 'field-required',
        
        inputCls: null
    },
    noWrapCls: Ext.baseCSSPrefix + 'form-label-nowrap',
    
    getElementConfig: function() {
        var prefix = Ext.baseCSSPrefix;
        return {
            reference: 'element',
            className: Ext.baseCSSPrefix + 'container',
            children: [
                {
                    reference: 'label',
                    cls: prefix + 'form-label',
                    children: [
                        {
                            reference: 'labelspan',
                            tag: 'span'
                        }
                    ]
                },
                {
                    reference: 'innerElement',
                    cls: prefix + 'component-outer'
                }
            ]
        };
    },
    
    updateLabel: function(newLabel, oldLabel) {
        var renderElement = this.renderElement,
            prefix = Ext.baseCSSPrefix;
        if (newLabel) {
            this.labelspan.setHtml(newLabel);
            renderElement.addCls(prefix + 'field-labeled');
        } else {
            renderElement.removeCls(prefix + 'field-labeled');
        }
    },
    
    updateLabelAlign: function(newLabelAlign, oldLabelAlign) {
        var renderElement = this.renderElement,
            prefix = Ext.baseCSSPrefix;
        if (newLabelAlign) {
            renderElement.addCls(prefix + 'label-align-' + newLabelAlign);
            if (newLabelAlign == "top" || newLabelAlign == "bottom") {
                this.label.setWidth('100%');
            } else {
                this.updateLabelWidth(this.getLabelWidth());
            }
        }
        if (oldLabelAlign) {
            renderElement.removeCls(prefix + 'label-align-' + oldLabelAlign);
        }
    },
    
    updateLabelCls: function(newLabelCls, oldLabelCls) {
        if (newLabelCls) {
            this.label.addCls(newLabelCls);
        }
        if (oldLabelCls) {
            this.label.removeCls(oldLabelCls);
        }
    },
    
    updateLabelWidth: function(newLabelWidth) {
        var labelAlign = this.getLabelAlign();
        if (newLabelWidth) {
            if (labelAlign == "top" || labelAlign == "bottom") {
                this.label.setWidth('100%');
            } else {
                this.label.setWidth(newLabelWidth);
            }
        }
    },
    
    updateLabelWrap: function(newLabelWrap, oldLabelWrap) {
        this.toggleCls(this.noWrapCls, !newLabelWrap);
    },
    
    updateRequired: function(newRequired) {
        this.renderElement.toggleCls(this.getRequiredCls(), newRequired);
    },
    
    updateRequiredCls: function(newRequiredCls, oldRequiredCls) {
        if (this.getRequired()) {
            this.renderElement.replaceCls(oldRequiredCls, newRequiredCls);
        }
    },
    
    initialize: function() {
        var me = this;
        me.callParent();
        me.doInitValue();
    },
    
    doInitValue: function() {
        
        this.originalValue = this.getInitialConfig().value;
    },
    
    reset: function() {
        this.setValue(this.originalValue);
        return this;
    },
    
    resetOriginalValue: function() {
        this.originalValue = this.getValue();
    },
    
    isDirty: function() {
        return false;
    }
});


Ext.define('Ext.field.Text', {
    extend: Ext.field.Field,
    xtype: 'textfield',
    alternateClassName: 'Ext.form.Text',
    
    
    
    
    
    
    
    
    config: {
        
        ui: 'text',
        
        clearIcon: true,
        
        placeHolder: null,
        
        maxLength: null,
        
        autoComplete: null,
        
        autoCapitalize: null,
        
        autoCorrect: null,
        
        readOnly: null,
        
        component: {
            xtype: 'input',
            type: 'text',
            fastFocus: false
        },
        bubbleEvents: [
            'action'
        ]
    },
    defaultBindProperty: 'value',
    twoWayBindable: {
        value: 1
    },
    publishes: {
        value: 1
    },
    focusedCls: Ext.baseCSSPrefix + 'field-focused',
    clearableCls: Ext.baseCSSPrefix + 'field-clearable',
    emptyCls: Ext.baseCSSPrefix + 'empty',
    
    initialize: function() {
        var me = this;
        me.callParent();
        me.getComponent().on({
            scope: this,
            keyup: 'onKeyUp',
            input: 'onInput',
            focus: 'onFocus',
            blur: 'onBlur',
            paste: 'onPaste',
            mousedown: 'onMouseDown',
            clearicontap: 'onClearIconTap'
        });
        
        me.originalValue = me.getValue() || "";
        me.getComponent().originalValue = me.originalValue;
        me.syncEmptyCls();
    },
    syncEmptyCls: function() {
        var val = this._value,
            empty = val ? val.length : false;
        this.toggleCls(this.emptyCls, !empty);
    },
    applyValue: function(value) {
        return Ext.isEmpty(value) ? '' : value;
    },
    
    updateValue: function(value, oldValue) {
        var me = this,
            component = me.getComponent(),
            
            valueValid = value !== undefined && value !== null && value !== '';
        if (component) {
            component.setValue(value);
        }
        me.toggleClearIcon(valueValid && me.isDirty());
        me.syncEmptyCls();
        if (me.initialized) {
            me.fireEvent('change', me, value, oldValue);
        }
    },
    
    updatePlaceHolder: function(newPlaceHolder) {
        this.getComponent().setPlaceHolder(newPlaceHolder);
    },
    
    updateMaxLength: function(newMaxLength) {
        this.getComponent().setMaxLength(newMaxLength);
    },
    
    updateAutoComplete: function(newAutoComplete) {
        this.getComponent().setAutoComplete(newAutoComplete);
    },
    
    updateAutoCapitalize: function(newAutoCapitalize) {
        this.getComponent().setAutoCapitalize(newAutoCapitalize);
    },
    
    updateAutoCorrect: function(newAutoCorrect) {
        this.getComponent().setAutoCorrect(newAutoCorrect);
    },
    
    updateReadOnly: function(newReadOnly) {
        this.toggleClearIcon(!newReadOnly);
        this.getComponent().setReadOnly(newReadOnly);
    },
    
    updateInputType: function(newInputType) {
        var component = this.getComponent();
        if (component) {
            component.setType(newInputType);
        }
    },
    
    updateName: function(newName) {
        var component = this.getComponent();
        if (component) {
            component.setName(newName);
        }
    },
    
    updateTabIndex: function(newTabIndex) {
        var component = this.getComponent();
        if (component) {
            component.setTabIndex(newTabIndex);
        }
    },
    
    updateInputCls: function(newInputCls, oldInputCls) {
        var component = this.getComponent();
        if (component) {
            component.replaceCls(oldInputCls, newInputCls);
        }
    },
    updateDisabled: function(disabled, oldDisabled) {
        this.callParent([
            disabled,
            oldDisabled
        ]);
        var component = this.getComponent();
        if (component) {
            component.setDisabled(disabled);
        }
        this.toggleClearIcon(!disabled);
    },
    
    showClearIcon: function() {
        var me = this,
            value = me.getValue(),
            
            valueValid = value !== undefined && value !== null && value !== "";
        if (me.getClearIcon() && !me.getDisabled() && !me.getReadOnly() && valueValid) {
            me.element.addCls(me.clearableCls);
        }
        return me;
    },
    
    hideClearIcon: function() {
        if (this.getClearIcon()) {
            this.element.removeCls(this.clearableCls);
        }
    },
    onKeyUp: function(e) {
        this.fireAction('keyup', [
            this,
            e
        ], 'doKeyUp');
    },
    
    doKeyUp: function(me, e) {
        
        var value = me.getValue(),
            
            valueValid = value !== undefined && value !== null && value !== '';
        me.toggleClearIcon(valueValid);
        if (e.browserEvent.keyCode === 13) {
            me.fireAction('action', [
                me,
                e
            ], 'doAction');
        }
    },
    doAction: function() {
        this.blur();
    },
    onClearIconTap: function(input, e) {
        this.fireAction('clearicontap', [
            this,
            input,
            e
        ], 'doClearIconTap');
    },
    
    doClearIconTap: function(me, e) {
        me.setValue('');
    },
    onInput: function(component, value) {
        this.setValue(value);
    },
    onFocus: function(e) {
        var me = this;
        me.addCls(me.focusedCls);
        me.isFocused = true;
        me.fireEvent('focus', me, e);
    },
    onBlur: function(e) {
        var me = this;
        me.removeCls(me.focusedCls);
        me.isFocused = false;
        me.fireEvent('blur', me, e);
        Ext.defer(function() {
            me.isFocused = false;
        }, 50);
    },
    onPaste: function(e) {
        this.fireEvent('paste', this, e);
    },
    onMouseDown: function(e) {
        this.fireEvent('mousedown', this, e);
    },
    
    focus: function() {
        this.getComponent().focus();
        return this;
    },
    
    blur: function() {
        this.getComponent().blur();
        return this;
    },
    
    select: function() {
        this.getComponent().select();
        return this;
    },
    resetOriginalValue: function() {
        var me = this,
            comp;
        me.callParent();
        component = me.getComponent();
        if (component && component.hasOwnProperty("originalValue")) {
            me.getComponent().originalValue = me.originalValue;
        }
        me.reset();
    },
    reset: function() {
        var me = this;
        me.getComponent().reset();
        
        me.getValue();
        me.toggleClearIcon(me.isDirty());
    },
    isDirty: function() {
        var component = this.getComponent();
        if (component) {
            return component.isDirty();
        }
        return false;
    },
    privates: {
        toggleClearIcon: function(state) {
            if (state) {
                this.showClearIcon();
            } else {
                this.hideClearIcon();
            }
        }
    }
});


Ext.define('Ext.field.TextAreaInput', {
    extend: Ext.field.Input,
    xtype: 'textareainput',
    tag: 'textarea'
});


Ext.define('Ext.field.TextArea', {
    extend: Ext.field.Text,
    xtype: 'textareafield',
    alternateClassName: 'Ext.form.TextArea',
    config: {
        
        ui: 'textarea',
        
        autoCapitalize: false,
        
        component: {
            xtype: 'textareainput'
        },
        
        maxRows: null
    },
    
    updateMaxRows: function(newRows) {
        this.getComponent().setMaxRows(newRows);
    },
    updateHeight: function(height, oldHeight) {
        this.callParent([
            height,
            oldHeight
        ]);
        this.getComponent().input.setHeight(height);
    },
    updateWidth: function(width, oldWidth) {
        this.callParent([
            width,
            oldWidth
        ]);
        this.getComponent().input.setWidth(width);
    },
    
    doKeyUp: function(me) {
        
        this.toggleClearIcon(this.getValue());
    }
});


Ext.define('Ext.MessageBox', {
    extend: Ext.Sheet,
    xtype: 'messagebox',
    config: {
        
        baseCls: Ext.baseCSSPrefix + 'msgbox',
        
        iconCls: null,
        
        showAnimation: {
            type: 'popIn',
            duration: 250,
            easing: 'ease-out'
        },
        
        hideAnimation: {
            type: 'popOut',
            duration: 250,
            easing: 'ease-out'
        },
        
        zIndex: 999,
        
        defaultTextHeight: 75,
        
        title: null,
        
        buttons: null,
        
        message: null,
        
        
        prompt: null,
        
        modal: true,
        
        layout: {
            type: 'vbox',
            pack: 'center'
        }
    },
    statics: {
        OK: {
            text: 'OK',
            itemId: 'ok',
            ui: 'action'
        },
        YES: {
            text: 'Yes',
            itemId: 'yes',
            ui: 'action'
        },
        NO: {
            text: 'No',
            itemId: 'no'
        },
        CANCEL: {
            text: 'Cancel',
            itemId: 'cancel'
        },
        INFO: Ext.baseCSSPrefix + 'msgbox-info',
        WARNING: Ext.baseCSSPrefix + 'msgbox-warning',
        QUESTION: Ext.baseCSSPrefix + 'msgbox-question',
        ERROR: Ext.baseCSSPrefix + 'msgbox-error',
        OKCANCEL: [
            {
                text: 'Cancel',
                itemId: 'cancel'
            },
            {
                text: 'OK',
                itemId: 'ok',
                ui: 'action'
            }
        ],
        YESNOCANCEL: [
            {
                text: 'Cancel',
                itemId: 'cancel'
            },
            {
                text: 'No',
                itemId: 'no'
            },
            {
                text: 'Yes',
                itemId: 'yes',
                ui: 'action'
            }
        ],
        YESNO: [
            {
                text: 'No',
                itemId: 'no'
            },
            {
                text: 'Yes',
                itemId: 'yes',
                ui: 'action'
            }
        ]
    },
    
    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") {
            config = {
                title: config
            };
        }
        Ext.applyIf(config, {
            docked: 'top',
            ui: Ext.filterPlatform('blackberry') ? 'light' : null,
            cls: this.getBaseCls() + '-title'
        });
        if (Ext.theme.is.Tizen) {
            Ext.applyIf(config, {
                centered: false
            });
        }
        return Ext.factory(config, Ext.Toolbar, this.getTitle());
    },
    
    updateTitle: function(newTitle) {
        if (newTitle) {
            this.add(newTitle);
        }
    },
    
    updateButtons: function(newButtons) {
        var me = this;
        
        
        newButtons = (!newButtons || newButtons.length === 0) ? false : newButtons;
        if (newButtons) {
            if (me.buttonsToolbar) {
                me.buttonsToolbar.show();
                me.buttonsToolbar.removeAll();
                me.buttonsToolbar.setItems(newButtons);
            } else {
                var layout = {
                        type: 'hbox',
                        pack: 'center'
                    };
                var isFlexed = Ext.theme.is.MountainView || Ext.theme.is.Blackberry;
                me.buttonsToolbar = Ext.create('Ext.Toolbar', {
                    docked: 'bottom',
                    defaultType: 'button',
                    defaults: {
                        flex: (isFlexed) ? 1 : undefined,
                        ui: (Ext.theme.is.Blackberry) ? 'action' : undefined
                    },
                    layout: layout,
                    ui: me.getUi(),
                    cls: me.getBaseCls() + '-buttons',
                    items: newButtons
                });
                me.add(me.buttonsToolbar);
            }
        } else if (me.buttonsToolbar) {
            me.buttonsToolbar.hide();
        }
    },
    
    applyMessage: function(config) {
        config = {
            html: config,
            cls: this.getBaseCls() + '-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) {
        config = {
            xtype: 'component',
            docked: 'left',
            width: 40,
            height: 40,
            baseCls: Ext.baseCSSPrefix + 'icon',
            hidden: (config) ? false : true,
            cls: config
        };
        return Ext.factory(config, Ext.Component, this._iconCls);
    },
    
    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) {
            this.remove(oldPrompt);
        }
    },
    
    onClick: function(button) {
        if (button) {
            var config = button.config.userConfig || {},
                initialConfig = button.getInitialConfig(),
                prompt = this.getPrompt();
            if (typeof config.fn == 'function') {
                button.disable();
                this.on({
                    hiddenchange: function() {
                        config.fn.call(config.scope || null, initialConfig.itemId || initialConfig.text, prompt ? prompt.getValue() : null, config);
                        button.enable();
                    },
                    single: true,
                    scope: this
                });
            }
            if (config.input) {
                config.input.dom.blur();
            }
        }
        this.hide();
    },
    
    show: function(initialConfig) {
        Ext.util.InputBlocker.blockInputs();
        
        if (!this.getParent() && Ext.Viewport) {
            Ext.Viewport.add(this);
        }
        if (!initialConfig) {
            return this.callParent();
        }
        var config = Ext.apply({}, initialConfig),
            buttons = initialConfig.buttons || Ext.MessageBox.OK || [],
            buttonBarItems = [],
            userConfig = initialConfig;
        Ext.each(buttons, function(buttonConfig) {
            if (!buttonConfig) {
                return;
            }
            buttonBarItems.push(Ext.apply({
                userConfig: userConfig,
                scope: this,
                handler: 'onClick'
            }, buttonConfig));
        }, this);
        config.buttons = buttonBarItems;
        config.prompt = config.prompt || null;
        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({}, this.defaultAllowedConfig, config);
        this.setConfig(config);
        var prompt = this.getPrompt();
        if (prompt) {
            prompt.setValue(initialConfig.value || '');
        }
        this.callParent();
        return this;
    },
    
    alert: function(title, message, fn, scope) {
        return this.show({
            title: title || null,
            message: message || null,
            buttons: Ext.MessageBox.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,
            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,
            multiLine: multiLine,
            value: value,
            fn: function() {
                if (fn) {
                    Ext.callback(fn, scope, arguments);
                }
            }
        });
    }
}, function(MessageBox) {
    Ext.onInternalReady(function() {
        
        Ext.Msg = new MessageBox();
    });
});


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.ProgressIndicator', {
    extend: Ext.Container,
    mixins: [
        Ext.mixin.Progressable
    ],
    xtype: 'progressindicator',
    config: {
        baseCls: Ext.baseCSSPrefix + 'progressindicator',
        hidden: true,
        modal: true,
        centered: true,
        
        loadingText: {
            any: 'Loading: {percent}%',
            upload: 'Uploading: {percent}%',
            download: 'Downloading: {percent}%'
        },
        
        fallbackText: {
            any: 'Loading',
            upload: 'Uploading',
            download: 'Downloading'
        },
        
        monitoredStates: {
            upload: true,
            download: true
        },
        showAnimation: !Ext.browser.is.AndroidStock ? {
            type: 'slideIn',
            direction: "left",
            duration: 250,
            easing: 'ease-out'
        } : null,
        hideAnimation: !Ext.browser.is.AndroidStock ? {
            type: 'slideOut',
            direction: "left",
            duration: 250,
            easing: 'ease-in'
        } : null,
        
        minProgressOutput: 0,
        
        maxProgressOutput: 1,
        
        state: null
    },
    constructor: function() {
        this.emptyTpl = new Ext.XTemplate("");
        this.callParent(arguments);
    },
    getElementConfig: function() {
        return {
            reference: 'element',
            classList: [
                'x-container',
                'x-unsized'
            ],
            children: [
                {
                    reference: 'innerElement',
                    className: Ext.baseCSSPrefix + 'progressindicator-inner',
                    children: [
                        {
                            reference: 'progressBarText',
                            className: Ext.baseCSSPrefix + 'progressindicator-text'
                        },
                        {
                            reference: 'progressBar',
                            className: Ext.baseCSSPrefix + 'progressindicator-bar',
                            children: [
                                {
                                    reference: 'progressBarFill',
                                    className: Ext.baseCSSPrefix + 'progressindicator-bar-fill'
                                }
                            ]
                        }
                    ]
                }
            ]
        };
    },
    onStartProgress: function() {
        if (!this.getParent()) {
            Ext.Viewport.add(this);
        }
        this.show();
    },
    onEndProgress: function() {
        this.hide();
    },
    onUpdateProgress: function() {
        this.updateBar();
    },
    getLoadingText: function() {
        var state = this.getState();
        if (this._loadingText[state]) {
            return this._loadingText[state];
        }
        if (this._loadingText["any"]) {
            return this._loadingText["any"];
        }
        return this.emptyTpl;
    },
    applyLoadingText: function(loadingText) {
        var tpl = {},
            property, value;
        if (Ext.isString(loadingText)) {
            tpl = {
                any: new Ext.XTemplate(loadingText)
            };
        } else if (loadingText instanceof Ext.XTemplate) {
            tpl = {
                any: loadingText
            };
        } else {
            for (property in loadingText) {
                value = loadingText[property];
                tpl[property] = new Ext.XTemplate(value);
            }
        }
        if (!tpl.any) {
            tpl.any = this.emptyTpl;
        }
        return tpl;
    },
    getFallbackText: function() {
        var state = this.getState();
        if (this._fallbackText[state]) {
            return this._fallbackText[state];
        }
        if (this._fallbackText["any"]) {
            return this._fallbackText["any"];
        }
        return "";
    },
    applyFallbackText: function(fallbackText) {
        var obj = {},
            property, value;
        if (Ext.isString(fallbackText)) {
            obj = {
                any: fallbackText
            };
        } else {
            for (property in fallbackText) {
                value = fallbackText[property];
                obj[property] = value;
            }
        }
        if (!obj.any) {
            obj.any = this.emptyTpl;
        }
        return obj;
    },
    updateDynamic: function(value) {
        if (!value) {
            this.progressBarText.setHtml(this.getFallbackText());
            this.progressBar.setWidth("100%");
        } else {
            this.updateBar();
        }
        return value;
    },
    updateBar: function() {
        var state = this.getState();
        if (this.getMonitoredStates()[state] !== true) {
            this.progressBarText.setHtml(this.getFallbackText());
            this.progressBar.setWidth("100%");
            return;
        }
        var percent = this.getProgress() * 100;
        if (!Ext.isNumber(percent))  {
            percent = 0;
        }
        
        this.progressBar.setWidth(percent + "%");
        var loadingText = this.getLoadingText();
        if (loadingText) {
            this.progressBarText.setHtml(this.getLoadingText().apply({
                state: state,
                percent: Math.ceil(percent) || 0
            }));
        } else {
            this.progressBarText.setHtml('');
        }
    }
});


Ext.define('Ext.SegmentedButton', {
    extend: Ext.Container,
    xtype: 'segmentedbutton',
    alternateClassName: 'Ext.button.Segmented',
    isSegmentedButton: true,
    config: {
        
        baseCls: Ext.baseCSSPrefix + 'segmentedbutton',
        
        pressedCls: Ext.baseCSSPrefix + 'button-pressed',
        
        allowMultiple: false,
        
        allowDepress: false,
        
        allowToggle: true,
        
        pressedButtons: [],
        
        layout: {
            type: 'hbox',
            align: 'stretch'
        },
        
        defaultType: 'button',
        
        defaultUI: null
    },
    
    initialize: function() {
        var me = this;
        me.callParent();
        me.on({
            delegate: '> button',
            scope: me,
            tap: 'onButtonRelease'
        });
        me.onAfter({
            delegate: '> button',
            scope: me,
            hide: 'onButtonHiddenChange',
            show: 'onButtonHiddenChange'
        });
    },
    updateAllowMultiple: function(allowMultiple) {
        if (!this.initialized && !this.getInitialConfig().hasOwnProperty('allowDepress') && allowMultiple) {
            this.setAllowDepress(true);
        }
    },
    
    applyItems: function() {
        var me = this,
            pressedButtons = [],
            ln, i, item, items;
        
        me.callParent(arguments);
        items = this.getItems();
        ln = items.length;
        for (i = 0; i < ln; i++) {
            item = items.items[i];
            if (item.getInitialConfig('pressed')) {
                pressedButtons.push(items.items[i]);
            }
        }
        me.updateFirstAndLastCls(items);
        me.setPressedButtons(pressedButtons);
    },
    
    onButtonRelease: function(button) {
        if (!this.getAllowToggle()) {
            return;
        }
        var me = this,
            pressedButtons = me.getPressedButtons() || [],
            buttons = [],
            alreadyPressed;
        if (!me.getDisabled() && !button.getDisabled()) {
            
            if (me.getAllowMultiple()) {
                buttons = pressedButtons.concat(buttons);
            }
            alreadyPressed = (buttons.indexOf(button) !== -1) || (pressedButtons.indexOf(button) !== -1);
            
            if (alreadyPressed && me.getAllowDepress()) {
                Ext.Array.remove(buttons, button);
            } else if (!alreadyPressed || !me.getAllowDepress()) {
                buttons.push(button);
            }
            me.setPressedButtons(buttons);
        }
    },
    onItemAdd: function() {
        this.callParent(arguments);
        this.updateFirstAndLastCls(this.getItems());
    },
    onItemRemove: function() {
        this.callParent(arguments);
        this.updateFirstAndLastCls(this.getItems());
    },
    
    onButtonHiddenChange: function() {
        this.updateFirstAndLastCls(this.getItems());
    },
    
    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;
            }
        }
    },
    
    applyPressedButtons: function(newButtons) {
        var me = this,
            array = [],
            button, ln, i;
        if (me.getAllowToggle()) {
            if (Ext.isArray(newButtons)) {
                ln = newButtons.length;
                for (i = 0; i < ln; i++) {
                    button = me.getComponent(newButtons[i]);
                    if (button && array.indexOf(button) === -1) {
                        array.push(button);
                    }
                }
            } else {
                button = me.getComponent(newButtons);
                if (button && array.indexOf(button) === -1) {
                    array.push(button);
                }
            }
        }
        return array;
    },
    
    updatePressedButtons: function(newButtons, oldButtons) {
        var me = this,
            items = me.getItems(),
            pressedCls = me.getPressedCls(),
            events = [],
            item, button, ln, i, e;
        
        ln = items.length;
        if (oldButtons && oldButtons.length) {
            for (i = 0; i < ln; i++) {
                item = items.items[i];
                if (oldButtons.indexOf(item) != -1 && newButtons.indexOf(item) == -1) {
                    item.removeCls([
                        pressedCls,
                        item.getPressedCls()
                    ]);
                    events.push({
                        item: item,
                        toggle: false
                    });
                }
            }
        }
        
        ln = newButtons.length;
        for (i = 0; i < ln; i++) {
            button = newButtons[i];
            if (!oldButtons || oldButtons.indexOf(button) == -1) {
                button.addCls(pressedCls);
                events.push({
                    item: button,
                    toggle: true
                });
            }
        }
        
        ln = events.length;
        if (ln && oldButtons !== undefined) {
            Ext.defer(function() {
                for (i = 0; i < ln; i++) {
                    e = events[i];
                    me.fireEvent('toggle', me, e.item, e.toggle);
                }
            }, 50);
        }
    },
    setPressed: function(button, pressed) {
        var pressedButtons = this.getPressedButtons().slice();
        if (pressed) {
            Ext.Array.include(pressedButtons, button);
        } else {
            Ext.Array.remove(pressedButtons, button);
        }
        this.setPressedButtons(pressedButtons);
    },
    
    isPressed: function(button) {
        var pressedButtons = this.getPressedButtons();
        return pressedButtons.indexOf(button) !== -1;
    },
    
    updateDisabled: function(disabled) {
        var me = this;
        me.items.each(function(item) {
            item.setDisabled(disabled);
        }, me);
        me.callParent(arguments);
    },
    setValue: function(value) {
        this.setPressedButtons([
            this.items.getAt(value)
        ]);
    },
    getValue: function() {
        var buttons = this.getPressedButtons(),
            out = -1;
        if (buttons.length) {
            out = this.items.indexOf(buttons[0]);
        }
        return out;
    },
    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);
            }
        }
    },
    doAdd: function(item, instanced) {
        var defaultUI = this.getDefaultUI();
        if (defaultUI && (item.getUi() == null)) {
            item.setUi(defaultUI);
        }
        this.callParent([
            item,
            instanced
        ]);
    }
});


Ext.define('Ext.Sortable', {
    mixins: {
        observable: Ext.mixin.Observable
    },
    config: {
        
        baseCls: Ext.baseCSSPrefix + 'sortable',
        
        delay: 0
    },
    
    direction: 'vertical',
    
    cancelSelector: null,
    
    
    
    
    
    constrain: window,
    
    group: 'base',
    
    revert: true,
    
    itemSelector: null,
    
    handleSelector: null,
    
    disabled: false,
    
    
    sorting: false,
    
    vertical: false,
    
    constructor: function(el, config) {
        config = config || {};
        Ext.apply(this, config);
        this.addEvents(
        'sortstart', 
        'sortend', 
        'sortchange');
        
        
        
        
        
        
        
        
        this.el = Ext.get(el);
        this.callParent();
        this.mixins.observable.constructor.call(this);
        if (this.direction == 'horizontal') {
            this.horizontal = true;
        } else if (this.direction == 'vertical') {
            this.vertical = true;
        } else {
            this.horizontal = this.vertical = true;
        }
        this.el.addCls(this.baseCls);
        this.startEventName = (this.getDelay() > 0) ? 'taphold' : 'tapstart';
        if (!this.disabled) {
            this.enable();
        }
    },
    
    onStart: function(e, t) {
        if (this.cancelSelector && e.getTarget(this.cancelSelector)) {
            return;
        }
        if (this.handleSelector && !e.getTarget(this.handleSelector)) {
            return;
        }
        if (!this.sorting) {
            this.onSortStart(e, t);
        }
    },
    
    onSortStart: function(e, t) {
        this.sorting = true;
        var draggable = Ext.create('Ext.util.Draggable', t, {
                threshold: 0,
                revert: this.revert,
                direction: this.direction,
                constrain: this.constrain === true ? this.el : this.constrain,
                animationDuration: 100
            });
        draggable.on({
            drag: this.onDrag,
            dragend: this.onDragEnd,
            scope: this
        });
        this.dragEl = t;
        this.calculateBoxes();
        if (!draggable.dragging) {
            draggable.onStart(e);
        }
        this.fireEvent('sortstart', this, e);
    },
    
    calculateBoxes: function() {
        this.items = [];
        var els = this.el.select(this.itemSelector, false),
            ln = els.length,
            i, item, el, box;
        for (i = 0; i < ln; i++) {
            el = els[i];
            if (el != this.dragEl) {
                item = Ext.fly(el).getRegion();
                item.el = el;
                this.items.push(item);
            }
        }
    },
    
    onDrag: function(draggable, e) {
        var items = this.items,
            ln = items.length,
            region = draggable.region,
            sortChange = false,
            i, intersect, overlap, item;
        for (i = 0; i < ln; i++) {
            item = items[i];
            intersect = region.intersect(item);
            if (intersect) {
                if (this.vertical && Math.abs(intersect.top - intersect.bottom) > (region.bottom - region.top) / 2) {
                    if (region.bottom > item.top && item.top > region.top) {
                        draggable.el.insertAfter(item.el);
                    } else {
                        draggable.el.insertBefore(item.el);
                    }
                    sortChange = true;
                } else if (this.horizontal && Math.abs(intersect.left - intersect.right) > (region.right - region.left) / 2) {
                    if (region.right > item.left && item.left > region.left) {
                        draggable.el.insertAfter(item.el);
                    } else {
                        draggable.el.insertBefore(item.el);
                    }
                    sortChange = true;
                }
                if (sortChange) {
                    
                    draggable.reset();
                    
                    
                    draggable.moveTo(region.left, region.top);
                    
                    this.calculateBoxes();
                    this.fireEvent('sortchange', this, draggable.el, this.el.select(this.itemSelector, false).indexOf(draggable.el.dom));
                    return;
                }
            }
        }
    },
    
    onDragEnd: function(draggable, e) {
        draggable.destroy();
        this.sorting = false;
        this.fireEvent('sortend', this, draggable, e);
    },
    
    enable: function() {
        this.el.on(this.startEventName, this.onStart, this, {
            delegate: this.itemSelector,
            holdThreshold: this.getDelay()
        });
        this.disabled = false;
    },
    
    disable: function() {
        this.el.un(this.startEventName, this.onStart, this);
        this.disabled = true;
    },
    
    isDisabled: function() {
        return this.disabled;
    },
    
    isSorting: function() {
        return this.sorting;
    },
    
    isVertical: function() {
        return this.vertical;
    },
    
    isHorizontal: function() {
        return this.horizontal;
    }
});


Ext.define('Ext.TitleBar', {
    extend: Ext.Container,
    xtype: 'titlebar',
    defaultBindProperty: 'title',
    
    isToolbar: true,
    config: {
        
        baseCls: Ext.baseCSSPrefix + 'toolbar',
        
        cls: Ext.baseCSSPrefix + 'navigation-bar',
        
        ui: 'dark',
        
        title: null,
        
        titleAlign: 'center',
        
        defaultType: 'button',
        
        minHeight: null,
        
        layout: {
            type: 'hbox'
        },
        
        items: [],
        
        maxButtonWidth: '40%'
    },
    hasCSSMinHeight: true,
    beforeInitialize: function() {
        this.applyItems = this.applyInitialItems;
    },
    initialize: function() {
        delete this.applyItems;
        this.add(this.initialItems);
        delete this.initialItems;
        this.on({
            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',
            layout: {
                type: 'hbox',
                align: 'center'
            },
            listeners: {
                resize: 'refreshTitlePosition',
                scope: me
            }
        });
        me.spacer = me.add({
            xtype: 'component',
            style: 'position: relative',
            flex: 1,
            listeners: {
                resize: 'refreshTitlePosition',
                scope: me
            }
        });
        me.rightBox = me.add({
            xtype: 'container',
            style: 'position: relative',
            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) {
        if (item.config.align == 'right') {
            this.rightBox.add(item);
        } else {
            this.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) {
        if (item.config.align == 'right') {
            this.rightBox.insert(index, item);
        } else {
            this.leftBox.insert(index, item);
        }
    },
    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: {
        
        ui: 'dark',
        
        baseCls: Ext.baseCSSPrefix + 'toast',
        
        showAnimation: {
            type: 'popIn',
            duration: 250,
            easing: 'ease-out'
        },
        
        hideAnimation: {
            type: 'popOut',
            duration: 250,
            easing: 'ease-out'
        },
        
        zIndex: 999,
        
        message: null,
        
        timeout: 1000,
        
        messageAnimation: true,
        
        hideOnMaskTap: true,
        
        modal: true,
        
        layout: {
            type: 'vbox',
            pack: 'center'
        }
    },
    
    applyMessage: function(config) {
        config = {
            html: config,
            cls: this.getBaseCls() + '-text'
        };
        return Ext.factory(config, Ext.Component, this._message);
    },
    
    updateMessage: function(newMessage) {
        if (newMessage) {
            this.add(newMessage);
        }
    },
    
    applyTimeout: function(timeout) {
        if (this._timeoutID) {
            clearTimeout(this._timeoutID);
            if (!Ext.isEmpty(timeout)) {
                this._timeoutID = setTimeout(Ext.bind(this.onTimeout, this), timeout);
            }
        }
        return timeout;
    },
    
    next: Ext.emptyFn,
    
    show: function(config) {
        var me = this,
            timeout = config.timeout,
            msgAnimation = me.getMessageAnimation(),
            message = me.getMessage();
        if (me.isRendered() && me.isHidden() === false) {
            config.timeout = null;
            message.onAfter({
                hiddenchange: function() {
                    me.setMessage(config.message);
                    message = me.getMessage();
                    message.onAfter({
                        hiddenchange: function() {
                            
                            this._timeoutID = true;
                            me.setTimeout(timeout);
                        },
                        scope: me,
                        single: true
                    });
                    message.show(msgAnimation);
                },
                scope: me,
                single: true
            });
            message.hide(msgAnimation);
        } else {
            Ext.util.InputBlocker.blockInputs();
            me.setConfig(config);
            
            if (!me.getParent() && Ext.Viewport) {
                Ext.Viewport.add(me);
            }
            if (!Ext.isEmpty(timeout)) {
                me._timeoutID = setTimeout(Ext.bind(me.onTimeout, me), timeout);
            }
            me.callParent(arguments);
        }
    },
    
    hide: function(animation) {
        clearTimeout(this._timeoutID);
        if (!this.next()) {
            this.callParent(arguments);
        }
    },
    
    onTimeout: function() {
        this.hide();
    }
}, function(Toast) {
    var _queue = [],
        _isToasting = false;
    function next() {
        var config = _queue.shift();
        if (config) {
            _isToasting = true;
            this.show(config);
        } else {
            _isToasting = false;
        }
        return _isToasting;
    }
    function getInstance() {
        if (!Ext.Toast._instance) {
            Ext.Toast._instance = Ext.create('Ext.Toast');
            Ext.Toast._instance.next = next;
        }
        return Ext.Toast._instance;
    }
    Ext.toast = function(message, timeout) {
        var toast = getInstance(),
            config = message;
        if (Ext.isString(message)) {
            config = {
                message: message,
                timeout: timeout
            };
        }
        if (config.timeout === undefined) {
            config.timeout = Ext.Toast.prototype.config.timeout;
        }
        _queue.push(config);
        if (!_isToasting) {
            toast.next();
        }
        return toast;
    };
});


Ext.define('Ext.Video', {
    extend: Ext.Media,
    xtype: 'video',
    config: {
        
        
        posterUrl: null,
        
        baseCls: Ext.baseCSSPrefix + 'video',
        
        controls: true
    },
    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();
        }
    },
    updateControls: function(value) {
        this.media.set({
            controls: value ? true : undefined
        });
    },
    onActivate: function() {
        this.media.setTop(0);
    },
    onDeactivate: function() {
        this.pause();
        this.media.setTop(-2000);
        this.ghost.show();
    },
    
    onGhostTap: function() {
        var me = this,
            media = this.media,
            ghost = this.ghost;
        media.show();
        
        
        ghost.hide();
        me.play();
    },
    
    onPause: function() {
        this.callParent(arguments);
        if (!this.isInlineVideo) {
            this.media.setTop(-2000);
            this.ghost.show();
        }
    },
    
    onPlay: function() {
        this.callParent(arguments);
        this.media.setTop(0);
    },
    
    updatePosterUrl: function(newUrl) {
        var ghost = this.ghost;
        if (ghost) {
            ghost.setStyle('background-image', 'url(' + newUrl + ')');
        }
    }
});




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


Ext.define('Ext.carousel.Indicator', {
    extend: Ext.Component,
    xtype: 'carouselindicator',
    config: {
        
        baseCls: Ext.baseCSSPrefix + 'carousel-indicator',
        direction: 'horizontal'
    },
    
    
    initialize: function() {
        this.callParent();
        this.indicators = [];
        this.element.on({
            tap: 'onTap',
            scope: this
        });
    },
    updateDirection: function(newDirection, oldDirection) {
        var baseCls = this.getBaseCls();
        this.element.replaceCls(oldDirection, newDirection, baseCls);
        if (newDirection === 'horizontal') {
            this.setBottom(0);
            this.setRight(null);
        } else {
            this.setRight(0);
            this.setBottom(null);
        }
    },
    addIndicator: function() {
        this.indicators.push(this.element.createChild({
            tag: 'span'
        }));
    },
    removeIndicator: function() {
        var indicators = this.indicators;
        if (indicators.length > 0) {
            indicators.pop().destroy();
        }
    },
    setActiveIndex: function(index) {
        var indicators = this.indicators,
            currentActiveIndex = this.activeIndex,
            currentActiveItem = indicators[currentActiveIndex],
            activeItem = indicators[index],
            baseCls = this.getBaseCls();
        if (currentActiveItem) {
            currentActiveItem.removeCls(baseCls, null, 'active');
        }
        if (activeItem) {
            activeItem.addCls(baseCls, null, 'active');
        }
        this.activeIndex = index;
        return this;
    },
    
    onTap: function(e) {
        var touch = e.touch,
            box = this.element.getBox(),
            centerX = box.left + (box.width / 2),
            centerY = box.top + (box.height / 2),
            direction = this.getDirection();
        if ((direction === 'horizontal' && touch.pageX >= centerX) || (direction === 'vertical' && touch.pageY >= centerY)) {
            this.fireEvent('next', this);
        } else {
            this.fireEvent('previous', this);
        }
    },
    destroy: function() {
        var indicators = this.indicators,
            i, ln, indicator;
        for (i = 0 , ln = indicators.length; i < ln; i++) {
            indicator = indicators[i];
            indicator.destroy();
        }
        indicators.length = 0;
        this.callParent();
    }
});


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: {
        
        
        baseCls: 'x-carousel',
        
        direction: 'horizontal',
        directionLock: false,
        animation: {
            duration: 250,
            easing: {
                type: 'ease-out'
            }
        },
        
        
        indicator: true,
        
        ui: 'dark',
        itemConfig: {},
        bufferSize: 1,
        itemLength: null
    },
    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',
            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(),
            innerElement = this.innerElement,
            items = this.carouselItems,
            ln = items.length,
            itemConfig = 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);
            }
            item.setLayoutSizeFlags(this.LAYOUT_BOTH);
            items.push(item);
            innerElement.append(item.renderElement);
            if (isRendered && item.setRendered(true)) {
                item.fireEvent('renderedchange', this, item, true);
            }
        }
        this.getTranslatable().setActiveIndex(size);
    },
    setRendered: function(rendered) {
        var wasRendered = this.rendered;
        if (rendered !== wasRendered) {
            this.rendered = rendered;
            var items = this.items.items,
                carouselItems = this.carouselItems,
                i, ln, item;
            for (i = 0 , ln = items.length; i < ln; i++) {
                item = items[i];
                if (!item.isInnerItem()) {
                    item.setRendered(rendered);
                }
            }
            for (i = 0 , ln = carouselItems.length; i < ln; i++) {
                carouselItems[i].setRendered(rendered);
            }
            return true;
        }
        return false;
    },
    onSizeChange: function() {
        this.refreshSizing();
        this.refreshCarouselItems();
        this.refreshActiveItem();
    },
    onItemAdd: function(item, index) {
        this.callParent(arguments);
        var innerIndex = this.getInnerItems().indexOf(item),
            indicator = this.getIndicator();
        if (indicator && item.isInnerItem()) {
            indicator.addIndicator();
        }
        if (innerIndex <= this.getActiveIndex()) {
            this.refreshActiveIndex();
        }
        if (this.isIndexDirty(innerIndex) && !this.isItemsInitializing) {
            this.refreshActiveItem();
        }
    },
    doItemLayoutAdd: function(item) {
        if (item.isInnerItem()) {
            return;
        }
        this.callParent(arguments);
    },
    onItemRemove: function(item, index) {
        this.callParent(arguments);
        var innerIndex = this.getInnerItems().indexOf(item),
            indicator = this.getIndicator(),
            carouselItems = this.carouselItems,
            i, ln, carouselItem;
        if (item.isInnerItem() && indicator) {
            indicator.removeIndicator();
        }
        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);
                }
            }
            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,
            directionLock = this.getDirectionLock();
        this.isDragging = true;
        if (directionLock) {
            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);
    },
    applyAnimation: function(animation) {
        animation.easing = Ext.factory(animation.easing, Ext.fx.easing.EaseOut);
        return animation;
    },
    updateDirection: function(direction) {
        var indicator = this.getIndicator();
        this.currentAxis = (direction === 'horizontal') ? 'x' : 'y';
        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) {
        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() {
        var activeItem = this.callParent(arguments),
            activeIndex;
        if (activeItem) {
            activeIndex = this.getInnerItemIndex(activeItem);
            if (activeIndex !== -1) {
                this.activeIndex = activeIndex;
                return activeItem;
            }
        }
    },
    updateActiveItem: function(activeItem) {
        var activeIndex = this.getActiveIndex(),
            maxIndex = this.getMaxItemIndex(),
            indicator = this.getIndicator(),
            bufferSize = this.getBufferSize(),
            carouselItems = this.carouselItems.slice(),
            orderedCarouselItems = this.orderedCarouselItems,
            visibleIndexes = {},
            visibleItems = {},
            visibleItem, component, id, i, index, ln, carouselItem;
        if (carouselItems.length === 0) {
            return;
        }
        this.callParent(arguments);
        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 = this.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 = this.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;
                }
            }
        }
        this.inactiveCarouselItems.length = 0;
        this.inactiveCarouselItems = carouselItems;
        this.refreshOffset();
        this.refreshInactiveCarouselItems();
        if (indicator) {
            indicator.setActiveIndex(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.carousel.Indicator, currentIndicator);
    },
    
    updateIndicator: function(indicator) {
        if (indicator) {
            this.insertFirst(indicator);
            indicator.setUi(this.getUi());
            indicator.on({
                next: 'next',
                previous: 'previous',
                scope: this
            });
        }
    },
    destroy: function() {
        var me = this,
            carouselItems = me.carouselItems.slice();
        me.carouselItems.length = 0;
        Ext.destroy(carouselItems, me.getIndicator(), me.translatable);
        me.callParent();
        delete me.carouselItems;
    }
});


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.component.DataItem', {
    extend: Ext.Container,
    xtype: 'dataitem',
    config: {
        baseCls: Ext.baseCSSPrefix + 'data-item',
        defaultType: 'component',
        
        record: null,
        
        itemCls: null,
        
        dataMap: {},
        
        dataview: null,
        width: '100%',
        items: [
            {
                xtype: 'component'
            }
        ]
    },
    updateBaseCls: function(newBaseCls, oldBaseCls) {
        var me = this;
        me.callParent(arguments);
    },
    updateItemCls: function(newCls, oldCls) {
        if (oldCls) {
            this.removeCls(oldCls);
        }
        if (newCls) {
            this.addCls(newCls);
        }
    },
    doMapData: function(dataMap, data, item) {
        var componentName, component, setterMap, setterName;
        for (componentName in dataMap) {
            setterMap = dataMap[componentName];
            component = this[componentName]();
            if (component) {
                for (setterName in setterMap) {
                    if (data && component[setterName] && data[setterMap[setterName]] !== undefined && data[setterMap[setterName]] !== null) {
                        component[setterName](data[setterMap[setterName]]);
                    }
                }
            }
        }
        if (item) {
            
            item.updateData(data);
        }
    },
    
    updateRecord: function(newRecord) {
        if (!newRecord) {
            return;
        }
        this._record = newRecord;
        var me = this,
            dataview = me.dataview || this.getDataview(),
            data = dataview.prepareData(newRecord.getData(true), dataview.getStore().indexOf(newRecord), newRecord),
            items = me.getItems(),
            item = items.first(),
            dataMap = me.getDataMap();
        if (!item) {
            return;
        }
        if (dataMap) {
            this.doMapData(dataMap, data, item);
        }
        
        me.fireEvent('updatedata', me, data);
    }
});


Ext.define('Ext.dataview.component.Container', {
    extend: Ext.Container,
    
    
    
    
    
    
    
    
    constructor: function() {
        this.itemCache = [];
        this.callParent(arguments);
    },
    
    doInitialize: function() {
        this.innerElement.on({
            touchstart: 'onItemTouchStart',
            touchend: 'onItemTouchEnd',
            tap: 'onItemTap',
            taphold: 'onItemTapHold',
            touchmove: 'onItemTouchMove',
            singletap: 'onItemSingleTap',
            doubletap: 'onItemDoubleTap',
            swipe: 'onItemSwipe',
            delegate: '> .' + Ext.baseCSSPrefix + 'data-item',
            scope: this
        });
    },
    
    initialize: function() {
        this.callParent();
        this.doInitialize();
    },
    onItemTouchStart: function(e) {
        var me = this,
            target = e.currentTarget,
            item = Ext.getCmp(target.id);
        item.on({
            touchmove: 'onItemTouchMove',
            scope: me,
            single: true
        });
        me.fireEvent('itemtouchstart', me, item, me.indexOf(item), e);
    },
    onItemTouchMove: function(e) {
        var me = this,
            target = e.currentTarget,
            item = Ext.getCmp(target.id);
        me.fireEvent('itemtouchmove', me, item, me.indexOf(item), e);
    },
    onItemTouchEnd: function(e) {
        var me = this,
            target = e.currentTarget,
            item = Ext.getCmp(target.id);
        item.un({
            touchmove: 'onItemTouchMove',
            scope: me
        });
        me.fireEvent('itemtouchend', me, item, me.indexOf(item), e);
    },
    onItemTap: function(e) {
        var me = this,
            target = e.currentTarget,
            item = Ext.getCmp(target.id);
        me.fireEvent('itemtap', me, item, me.indexOf(item), e);
    },
    onItemTapHold: function(e) {
        var me = this,
            target = e.currentTarget,
            item = Ext.getCmp(target.id);
        me.fireEvent('itemtaphold', me, item, me.indexOf(item), e);
    },
    onItemSingleTap: function(e) {
        var me = this,
            target = e.currentTarget,
            item = Ext.getCmp(target.id);
        me.fireEvent('itemsingletap', me, item, me.indexOf(item), e);
    },
    onItemDoubleTap: function(e) {
        var me = this,
            target = e.currentTarget,
            item = Ext.getCmp(target.id);
        me.fireEvent('itemdoubletap', me, item, me.indexOf(item), e);
    },
    onItemSwipe: function(e) {
        var me = this,
            target = e.currentTarget,
            item = Ext.getCmp(target.id);
        me.fireEvent('itemswipe', me, item, me.indexOf(item), e);
    },
    moveItemsToCache: function(from, to) {
        var me = this,
            dataview = me.dataview,
            maxItemCache = dataview.getMaxItemCache(),
            items = me.getViewItems(),
            itemCache = me.itemCache,
            cacheLn = itemCache.length,
            pressedCls = dataview.getPressedCls(),
            selectedCls = dataview.getSelectedCls(),
            i = to - from,
            item;
        for (; i >= 0; i--) {
            item = items[from + i];
            if (cacheLn !== maxItemCache) {
                me.remove(item, false);
                item.removeCls([
                    pressedCls,
                    selectedCls
                ]);
                itemCache.push(item);
                cacheLn++;
            } else {
                item.destroy();
            }
        }
        if (me.getViewItems().length == 0) {
            this.dataview.showEmptyText();
        }
    },
    moveItemsFromCache: function(records) {
        var me = this,
            dataview = me.dataview,
            store = dataview.getStore(),
            ln = records.length,
            xtype = dataview.getDefaultType(),
            itemConfig = dataview.getItemConfig(),
            itemCache = me.itemCache,
            cacheLn = itemCache.length,
            items = [],
            i, item, record;
        if (ln) {
            dataview.hideEmptyText();
        }
        for (i = 0; i < ln; i++) {
            records[i]._tmpIndex = store.indexOf(records[i]);
        }
        Ext.Array.sort(records, function(record1, record2) {
            return record1._tmpIndex > record2._tmpIndex ? 1 : -1;
        });
        for (i = 0; i < ln; i++) {
            record = records[i];
            if (cacheLn) {
                cacheLn--;
                item = itemCache.pop();
                this.updateListItem(record, item);
            } else {
                item = me.getDataItemConfig(xtype, record, itemConfig);
            }
            item = this.insert(record._tmpIndex, item);
            delete record._tmpIndex;
        }
        return items;
    },
    getViewItems: function() {
        return this.getInnerItems();
    },
    updateListItem: function(record, item) {
        if (item.updateRecord) {
            if (item.getRecord() === record) {
                item.updateRecord(record);
            } else {
                item.setRecord(record);
            }
        }
    },
    getDataItemConfig: function(xtype, record, itemConfig) {
        var dataview = this.dataview,
            dataItemConfig = {
                xtype: xtype,
                record: record,
                itemCls: dataview.getItemCls(),
                defaults: itemConfig,
                dataview: dataview
            };
        return Ext.merge(dataItemConfig, itemConfig);
    },
    doRemoveItemCls: function(cls) {
        var items = this.getViewItems(),
            ln = items.length,
            i = 0;
        for (; i < ln; i++) {
            items[i].removeCls(cls);
        }
    },
    doAddItemCls: function(cls) {
        var items = this.getViewItems(),
            ln = items.length,
            i = 0;
        for (; i < ln; i++) {
            items[i].addCls(cls);
        }
    },
    updateAtNewIndex: function(oldIndex, newIndex, record) {
        this.moveItemsToCache(oldIndex, oldIndex);
        this.moveItemsFromCache([
            record
        ]);
    },
    destroy: function() {
        var me = this,
            itemCache = me.itemCache,
            ln = itemCache.length,
            i = 0;
        for (; i < ln; i++) {
            itemCache[i].destroy();
        }
        this.callParent();
    }
});


Ext.define('Ext.dataview.element.Container', {
    extend: Ext.Component,
    
    
    
    
    
    
    
    
    doInitialize: function() {
        this.element.on({
            touchstart: 'onItemTouchStart',
            touchend: 'onItemTouchEnd',
            tap: 'onItemTap',
            taphold: 'onItemTapHold',
            touchmove: 'onItemTouchMove',
            singletap: 'onItemSingleTap',
            doubletap: 'onItemDoubleTap',
            swipe: 'onItemSwipe',
            delegate: '> div',
            scope: this
        });
    },
    
    initialize: function() {
        this.callParent();
        this.doInitialize();
    },
    updateBaseCls: function(newBaseCls, oldBaseCls) {
        var me = this;
        me.callParent([
            newBaseCls + '-container',
            oldBaseCls
        ]);
    },
    onItemTouchStart: function(e) {
        var me = this,
            target = e.currentTarget,
            index = me.getViewItems().indexOf(target);
        Ext.get(target).on({
            touchmove: 'onItemTouchMove',
            scope: me,
            single: true
        });
        me.fireEvent('itemtouchstart', me, Ext.get(target), index, e);
    },
    onItemTouchEnd: function(e) {
        var me = this,
            target = e.currentTarget,
            index = me.getViewItems().indexOf(target);
        Ext.get(target).un({
            touchmove: 'onItemTouchMove',
            scope: me
        });
        me.fireEvent('itemtouchend', me, Ext.get(target), index, e);
    },
    onItemTouchMove: function(e) {
        var me = this,
            target = e.currentTarget,
            index = me.getViewItems().indexOf(target);
        me.fireEvent('itemtouchmove', me, Ext.get(target), index, e);
    },
    onItemTap: function(e) {
        var me = this,
            target = e.currentTarget,
            index = me.getViewItems().indexOf(target);
        me.fireEvent('itemtap', me, Ext.get(target), index, e);
    },
    onItemTapHold: function(e) {
        var me = this,
            target = e.currentTarget,
            index = me.getViewItems().indexOf(target);
        me.fireEvent('itemtaphold', me, Ext.get(target), index, e);
    },
    onItemDoubleTap: function(e) {
        var me = this,
            target = e.currentTarget,
            index = me.getViewItems().indexOf(target);
        me.fireEvent('itemdoubletap', me, Ext.get(target), index, e);
    },
    onItemSingleTap: function(e) {
        var me = this,
            target = e.currentTarget,
            index = me.getViewItems().indexOf(target);
        me.fireEvent('itemsingletap', me, Ext.get(target), index, e);
    },
    onItemSwipe: function(e) {
        var me = this,
            target = e.currentTarget,
            index = me.getViewItems().indexOf(target);
        me.fireEvent('itemswipe', me, Ext.get(target), index, e);
    },
    updateListItem: function(record, item) {
        var me = this,
            dataview = me.dataview,
            store = dataview.getStore(),
            index = store.indexOf(record),
            data = dataview.prepareData(record.getData(true), index, record);
        item.innerHTML = this.renderItemTpl(index, data, store);
    },
    addListItem: function(index, record) {
        var me = this,
            dataview = me.dataview,
            store = dataview.getStore(),
            data = dataview.prepareData(record.getData(true), index, record),
            element = me.element,
            childNodes = element.dom.childNodes,
            ln = childNodes.length,
            wrapElement;
        wrapElement = Ext.Element.create(this.getItemElementConfig(index, data, store));
        if (!ln || index == ln) {
            wrapElement.appendTo(element);
        } else {
            wrapElement.insertBefore(childNodes[index]);
        }
    },
    getItemElementConfig: function(index, data, store) {
        var dataview = this.dataview,
            itemCls = dataview.getItemCls(),
            cls = dataview.getBaseCls() + '-item';
        if (itemCls) {
            cls += ' ' + itemCls;
        }
        return {
            cls: cls,
            html: this.renderItemTpl(index, data, store)
        };
    },
    renderItemTpl: function(index, data, store) {
        var dataview = this.dataview,
            itemTpl = dataview.getItemTpl(),
            parent;
        store = store || dataview.getStore();
        parent = store.getData().items;
        data.xcount = typeof data.xcount === 'number' ? data.xcount : store.getCount();
        data.xindex = typeof data.xindex === 'number' ? data.xindex : index;
        return itemTpl.apply(data, parent, index + 1, parent.length);
    },
    doRemoveItemCls: function(cls) {
        var elements = this.getViewItems(),
            ln = elements.length,
            i = 0;
        for (; i < ln; i++) {
            Ext.fly(elements[i]).removeCls(cls);
        }
    },
    doAddItemCls: function(cls) {
        var elements = this.getViewItems(),
            ln = elements.length,
            i = 0;
        for (; i < ln; i++) {
            Ext.fly(elements[i]).addCls(cls);
        }
    },
    
    moveItemsToCache: function(from, to) {
        var me = this,
            items = me.getViewItems(),
            i = to - from,
            item;
        for (; i >= 0; i--) {
            item = items[from + i];
            Ext.get(item).destroy();
        }
        if (me.getViewItems().length == 0) {
            this.dataview.showEmptyText();
        }
    },
    
    moveItemsFromCache: function(records) {
        var me = this,
            dataview = me.dataview,
            store = dataview.getStore(),
            ln = records.length,
            i, record;
        if (ln) {
            dataview.hideEmptyText();
        }
        for (i = 0; i < ln; i++) {
            records[i]._tmpIndex = store.indexOf(records[i]);
        }
        Ext.Array.sort(records, function(record1, record2) {
            return record1._tmpIndex > record2._tmpIndex ? 1 : -1;
        });
        for (i = 0; i < ln; i++) {
            record = records[i];
            me.addListItem(record._tmpIndex, record);
            delete record._tmpIndex;
        }
    },
    
    getViewItems: function() {
        return Array.prototype.slice.call(this.element.dom.childNodes);
    },
    updateAtNewIndex: function(oldIndex, newIndex, record) {
        this.moveItemsToCache(oldIndex, oldIndex);
        this.moveItemsFromCache([
            record
        ]);
    },
    destroy: function() {
        var elements = this.getViewItems(),
            ln = elements.length,
            i = 0;
        for (; i < ln; i++) {
            Ext.get(elements[i]).destroy();
        }
        this.callParent();
    }
});


Ext.define('Ext.dataview.DataView', {
    extend: Ext.Container,
    alternateClassName: 'Ext.DataView',
    mixins: [
        Ext.mixin.Selectable
    ],
    xtype: 'dataview',
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    config: {
        
        
        store: null,
        
        data: null,
        
        baseCls: Ext.baseCSSPrefix + 'dataview',
        
        emptyText: null,
        
        deferEmptyText: true,
        
        itemTpl: '<div>{text}</div>',
        
        pressedCls: 'x-item-pressed',
        
        itemCls: null,
        
        selectedCls: 'x-item-selected',
        
        triggerEvent: 'itemtap',
        
        triggerCtEvent: 'tap',
        
        deselectOnContainerClick: true,
        
        scrollable: true,
        
        inline: null,
        
        pressedDelay: 100,
        
        loadingText: 'Loading...',
        
        useComponents: null,
        
        itemConfig: {},
        
        maxItemCache: 20,
        
        defaultType: 'dataitem',
        
        scrollToTopOnRefresh: true
    },
    defaultBindProperty: 'store',
    constructor: function(config) {
        var me = this;
        me.hasLoadedStore = false;
        me.mixins.selectable.constructor.apply(me, arguments);
        me.indexOffset = 0;
        me.callParent(arguments);
        
        var layout = this.getLayout();
        if (layout && !layout.isAuto) {
            Ext.Logger.error('The base layout for a DataView must always be an Auto Layout');
        }
        
        me.initSelectable();
    },
    updateItemCls: function(newCls, oldCls) {
        var container = this.container;
        if (container) {
            if (oldCls) {
                container.doRemoveItemCls(oldCls);
            }
            if (newCls) {
                container.doAddItemCls(newCls);
            }
        }
    },
    storeEventHooks: {
        beforeload: 'onBeforeLoad',
        groupchange: 'onStoreGroupChange',
        load: 'onLoad',
        refresh: 'refresh',
        add: 'onStoreAdd',
        remove: 'onStoreRemove',
        clear: 'onStoreClear',
        update: 'onStoreUpdate'
    },
    initialize: function() {
        this.callParent();
        var me = this,
            container,
            triggerEvent = me.getTriggerEvent();
        me.on(me.getTriggerCtEvent(), me.onContainerTrigger, me);
        container = me.container = this.add(new Ext.dataview[me.getUseComponents() ? 'component' : 'element'].Container({
            baseCls: this.getBaseCls()
        }));
        container.dataview = me;
        if (triggerEvent) {
            me.on(triggerEvent, me.onItemTrigger, me);
        }
        container.on({
            itemtouchstart: 'onItemTouchStart',
            itemtouchend: 'onItemTouchEnd',
            itemtap: 'onItemTap',
            itemtaphold: 'onItemTapHold',
            itemtouchmove: 'onItemTouchMove',
            itemsingletap: 'onItemSingleTap',
            itemdoubletap: 'onItemDoubleTap',
            itemswipe: 'onItemSwipe',
            scope: me
        });
        if (me.getStore()) {
            if (me.isPainted()) {
                me.refresh();
            } else {
                me.on({
                    painted: 'refresh',
                    single: true
                });
            }
        }
    },
    applyInline: function(config) {
        if (Ext.isObject(config)) {
            config = Ext.apply({}, config);
        }
        return config;
    },
    updateInline: function(newInline, oldInline) {
        var me = this,
            baseCls = me.getBaseCls();
        if (oldInline) {
            me.removeCls([
                baseCls + '-inlineblock',
                baseCls + '-nowrap'
            ]);
        }
        if (newInline) {
            me.addCls(baseCls + '-inlineblock');
            if (Ext.isObject(newInline) && newInline.wrap === false) {
                me.addCls(baseCls + '-nowrap');
            } else {
                me.removeCls(baseCls + '-nowrap');
            }
        }
    },
    
    prepareData: function(data, index, record) {
        return data;
    },
    
    onContainerTrigger: function(e) {
        var me = this;
        if (e.target != me.element.dom) {
            return;
        }
        if (me.getDeselectOnContainerClick() && me.getStore()) {
            me.deselectAll();
        }
    },
    
    onItemTrigger: function(me, index) {
        if (!this.destroyed) {
            this.selectWithEvent(this.getStore().getAt(index));
        }
    },
    doAddPressedCls: function(record) {
        var me = this,
            item = me.getItemAt(me.getStore().indexOf(record));
        if (Ext.isElement(item)) {
            item = Ext.get(item);
        }
        if (item) {
            if (item.isComponent) {
                item.renderElement.addCls(me.getPressedCls());
            } else {
                item.addCls(me.getPressedCls());
            }
        }
    },
    onItemTouchStart: function(container, target, index, e) {
        var me = this,
            store = me.getStore(),
            record = store && store.getAt(index);
        me.fireAction('itemtouchstart', [
            me,
            index,
            target,
            record,
            e
        ], 'doItemTouchStart');
    },
    doItemTouchStart: function(me, index, target, record) {
        var pressedDelay = me.getPressedDelay();
        if (record) {
            if (pressedDelay > 0) {
                me.pressedTimeout = Ext.defer(me.doAddPressedCls, pressedDelay, me, [
                    record
                ]);
            } else {
                me.doAddPressedCls(record);
            }
        }
    },
    onItemTouchEnd: function(container, target, index, e) {
        var me = this,
            store = me.getStore(),
            record = store && store.getAt(index);
        if (this.hasOwnProperty('pressedTimeout')) {
            clearTimeout(this.pressedTimeout);
            delete this.pressedTimeout;
        }
        if (record && target) {
            if (target.isComponent) {
                target.renderElement.removeCls(me.getPressedCls());
            } else {
                target.removeCls(me.getPressedCls());
            }
        }
        me.fireEvent('itemtouchend', me, index, target, record, e);
    },
    onItemTouchMove: function(container, target, index, e) {
        var me = this,
            store = me.getStore(),
            record = store && store.getAt(index);
        if (me.hasOwnProperty('pressedTimeout')) {
            clearTimeout(me.pressedTimeout);
            delete me.pressedTimeout;
        }
        if (record && target) {
            if (target.isComponent) {
                target.renderElement.removeCls(me.getPressedCls());
            } else {
                target.removeCls(me.getPressedCls());
            }
        }
        me.fireEvent('itemtouchmove', me, index, target, record, e);
    },
    onItemTap: function(container, target, index, e) {
        var me = this,
            store = me.getStore(),
            record = store && store.getAt(index);
        me.fireEvent('itemtap', me, index, target, record, e);
    },
    onItemTapHold: function(container, target, index, e) {
        var me = this,
            store = me.getStore(),
            record = store && store.getAt(index);
        me.fireEvent('itemtaphold', me, index, target, record, e);
    },
    onItemSingleTap: function(container, target, index, e) {
        var me = this,
            store = me.getStore(),
            record = store && store.getAt(index);
        me.fireEvent('itemsingletap', me, index, target, record, e);
    },
    onItemDoubleTap: function(container, target, index, e) {
        var me = this,
            store = me.getStore(),
            record = store && store.getAt(index);
        me.fireEvent('itemdoubletap', me, index, target, record, e);
    },
    onItemSwipe: function(container, target, index, e) {
        var me = this,
            store = me.getStore(),
            record = store && store.getAt(index);
        me.fireEvent('itemswipe', me, index, target, record, e);
    },
    
    onItemSelect: function(record, suppressEvent) {
        var me = this;
        if (suppressEvent) {
            me.doItemSelect(me, record);
        } else {
            me.fireAction('select', [
                me,
                record
            ], 'doItemSelect');
        }
    },
    
    doItemSelect: function(me, record) {
        if (me.container && !me.destroyed) {
            var item = me.getItemAt(me.getStore().indexOf(record));
            if (Ext.isElement(item)) {
                item = Ext.get(item);
            }
            if (item) {
                if (item.isComponent) {
                    item.renderElement.removeCls(me.getPressedCls());
                    item.renderElement.addCls(me.getSelectedCls());
                } else {
                    item.removeCls(me.getPressedCls());
                    item.addCls(me.getSelectedCls());
                }
            }
        }
    },
    
    onItemDeselect: function(record, suppressEvent) {
        var me = this;
        if (me.container && !me.destroyed) {
            if (suppressEvent) {
                me.doItemDeselect(me, record);
            } else {
                me.fireAction('deselect', [
                    me,
                    record,
                    suppressEvent
                ], 'doItemDeselect');
            }
        }
    },
    doItemDeselect: function(me, record) {
        var item = me.getItemAt(me.getStore().indexOf(record));
        if (Ext.isElement(item)) {
            item = Ext.get(item);
        }
        if (item) {
            if (item.isComponent) {
                item.renderElement.removeCls([
                    me.getPressedCls(),
                    me.getSelectedCls()
                ]);
            } else {
                item.removeCls([
                    me.getPressedCls(),
                    me.getSelectedCls()
                ]);
            }
        }
    },
    updateData: function(data) {
        var store = this.getStore();
        if (!store) {
            this.setStore(Ext.create('Ext.data.Store', {
                data: data,
                autoDestroy: true
            }));
        } else {
            store.add(data);
        }
    },
    applyStore: function(store) {
        var me = this,
            bindEvents = Ext.apply({}, me.storeEventHooks, {
                scope: me
            }),
            proxy, reader;
        if (store) {
            store = Ext.data.StoreManager.lookup(store);
            if (store && Ext.isObject(store) && store.isStore) {
                store.on(bindEvents);
                proxy = store.getProxy();
                if (proxy) {
                    reader = proxy.getReader();
                    if (reader) {
                        reader.on('exception', 'handleException', this);
                    }
                }
            } else 
            {
                Ext.Logger.warn("The specified Store cannot be found", this);
            }
        }
        
        return store;
    },
    
    handleException: function() {
        this.setMasked(false);
    },
    updateStore: function(newStore, oldStore) {
        var me = this,
            bindEvents = Ext.apply({}, me.storeEventHooks, {
                scope: me
            }),
            proxy, reader;
        if (oldStore && Ext.isObject(oldStore) && oldStore.isStore) {
            oldStore.un(bindEvents);
            if (!me.destroyed) {
                me.onStoreClear();
            }
            if (oldStore.getAutoDestroy()) {
                oldStore.destroy();
            } else {
                proxy = oldStore.getProxy();
                if (proxy) {
                    reader = proxy.getReader();
                    if (reader) {
                        reader.un('exception', 'handleException', this);
                    }
                }
            }
        }
        if (newStore) {
            if (newStore.isLoaded()) {
                this.hasLoadedStore = true;
            }
            if (newStore.isLoading()) {
                me.onBeforeLoad();
            }
            if (me.container) {
                me.refresh();
            }
        }
    },
    onBeforeLoad: function() {
        var me = this,
            loadingText = me.getLoadingText();
        if (loadingText && me.isPainted()) {
            me.setMasked({
                xtype: 'loadmask',
                message: loadingText
            });
        }
        me.hideEmptyText();
    },
    updateEmptyText: function(newEmptyText, oldEmptyText) {
        var me = this,
            store;
        if (oldEmptyText && me.emptyTextCmp) {
            me.remove(me.emptyTextCmp, true);
            delete me.emptyTextCmp;
        }
        if (newEmptyText) {
            me.emptyTextCmp = me.add({
                xtype: 'component',
                cls: me.getBaseCls() + '-emptytext',
                html: newEmptyText,
                hidden: true
            });
            store = me.getStore();
            if (store && me.hasLoadedStore && !store.getCount()) {
                me.showEmptyText();
            }
        }
    },
    onLoad: function(store) {
        
        this.hasLoadedStore = true;
        this.setMasked(false);
        if (!store.getCount()) {
            this.showEmptyText();
        }
    },
    
    refresh: function() {
        var me = this,
            container = me.container;
        if (!me.getStore()) {
            if (!me.hasLoadedStore && !me.getDeferEmptyText()) {
                me.showEmptyText();
            }
            return;
        }
        if (me.initialized && container) {
            me.fireAction('refresh', [
                me
            ], 'doRefresh');
        }
    },
    applyItemTpl: function(config) {
        return (Ext.isObject(config) && config.isTemplate) ? config : new Ext.XTemplate(config);
    },
    onAfterRender: function() {
        var me = this;
        me.updateStore(me.getStore());
    },
    
    getItemAt: function(index) {
        return this.getViewItems()[index - this.indexOffset];
    },
    
    getItemIndex: function(item) {
        var index = this.getViewItems().indexOf(item);
        return (index === -1) ? index : this.indexOffset + index;
    },
    
    getViewItems: function() {
        return this.container.getViewItems();
    },
    doRefresh: function(me) {
        var container = me.container,
            store = me.getStore(),
            records = store.getRange(),
            items = me.getViewItems(),
            recordsLn = records.length,
            itemsLn = items.length,
            deltaLn = recordsLn - itemsLn,
            scroller = me.getScrollable(),
            i, item;
        if (this.getScrollToTopOnRefresh() && scroller) {
            scroller.scrollTo(0, 0);
        }
        
        if (recordsLn < 1) {
            me.onStoreClear();
            return;
        } else {
            me.hideEmptyText();
        }
        
        if (deltaLn < 0) {
            container.moveItemsToCache(itemsLn + deltaLn, itemsLn - 1);
            
            items = me.getViewItems();
            itemsLn = items.length;
        }
        
        else if (deltaLn > 0) {
            container.moveItemsFromCache(store.getRange(itemsLn));
        }
        
        for (i = 0; i < itemsLn; i++) {
            item = items[i];
            container.updateListItem(records[i], item);
        }
        if (this.hasSelection()) {
            var selection = this.getSelection(),
                selectionLn = this.getSelectionCount(),
                record;
            for (i = 0; i < selectionLn; i++) {
                record = selection[i];
                this.doItemSelect(this, record);
            }
        }
    },
    showEmptyText: function() {
        if (this.getEmptyText() && (this.hasLoadedStore || !this.getDeferEmptyText())) {
            this.emptyTextCmp.show();
        }
    },
    hideEmptyText: function() {
        if (this.getEmptyText()) {
            this.emptyTextCmp.hide();
        }
    },
    destroy: function() {
        var store = this.getStore(),
            proxy = (store && store.getProxy()),
            reader = (proxy && proxy.getReader());
        if (reader) {
            
            
            reader.clearListeners();
        }
        this.callParent();
        this.setStore(null);
    },
    onStoreClear: function() {
        var me = this,
            container = me.container,
            items = me.getViewItems();
        container.moveItemsToCache(0, items.length - 1);
        this.showEmptyText();
    },
    
    onStoreGroupChange: Ext.emptyFn,
    
    onStoreAdd: function(store, records) {
        if (records) {
            this.hideEmptyText();
            this.container.moveItemsFromCache(records);
        }
    },
    
    onStoreRemove: function(store, records, indices) {
        var container = this.container,
            ln = records.length,
            i;
        for (i = 0; i < ln; i++) {
            container.moveItemsToCache(indices[i], indices[i]);
        }
    },
    
    onStoreUpdate: function(store, record, type, modifiedFieldNames, info) {
        var me = this,
            container = me.container,
            item;
        if (info.indexChanged) {
            container.updateAtNewIndex(info.oldIndex, info.newIndex, record);
            if (me.isSelected(record)) {
                me.doItemSelect(me, record);
            }
        } else {
            item = me.getViewItems()[me.getStore().indexOf(record)];
            if (item) {
                
                container.updateListItem(record, item);
            }
        }
    }
});


Ext.define('Ext.dataview.IndexBar', {
    extend: Ext.Component,
    alternateClassName: 'Ext.IndexBar',
    
    config: {
        baseCls: Ext.baseCSSPrefix + 'indexbar',
        
        direction: 'vertical',
        
        letters: [
            'A',
            'B',
            'C',
            'D',
            'E',
            'F',
            'G',
            'H',
            'I',
            'J',
            'K',
            'L',
            'M',
            'N',
            'O',
            'P',
            'Q',
            'R',
            'S',
            'T',
            'U',
            'V',
            'W',
            'X',
            'Y',
            'Z'
        ],
        ui: 'alphabet',
        
        listPrefix: null
    },
    
    itemCls: Ext.baseCSSPrefix + '',
    updateDirection: function(newDirection, oldDirection) {
        var baseCls = this.getBaseCls();
        this.element.replaceCls(baseCls + '-' + oldDirection, baseCls + '-' + newDirection);
    },
    getElementConfig: function() {
        
        if (Ext.theme.is.Blackberry) {
            return {
                reference: 'wrapper',
                classList: [
                    'x-centered',
                    'x-indexbar-wrapper'
                ],
                children: [
                    {
                        reference: 'indicator',
                        classList: [
                            'x-indexbar-indicator'
                        ],
                        hidden: true,
                        children: [
                            {
                                reference: 'indicatorInner',
                                classList: [
                                    'x-indexbar-indicator-inner'
                                ]
                            }
                        ]
                    },
                    this.callParent()
                ]
            };
        } else {
            return {
                reference: 'wrapper',
                classList: [
                    'x-centered',
                    'x-indexbar-wrapper'
                ],
                children: [
                    this.callParent()
                ]
            };
        }
    },
    updateLetters: function(letters) {
        this.innerElement.setHtml('');
        if (letters) {
            var ln = letters.length,
                i;
            for (i = 0; i < ln; i++) {
                this.innerElement.createChild({
                    html: letters[i]
                });
            }
        }
    },
    updateListPrefix: function(listPrefix) {
        if (listPrefix && listPrefix.length) {
            this.innerElement.createChild({
                html: listPrefix
            }, 0);
        }
    },
    
    initialize: function() {
        this.callParent();
        this.innerElement.on({
            touchstart: this.onTouchStart,
            touchend: this.onTouchEnd,
            dragend: this.onDragEnd,
            drag: this.onDrag,
            scope: this
        });
    },
    onTouchStart: function(e) {
        e.stopPropagation();
        this.innerElement.addCls(this.getBaseCls() + '-pressed');
        this.pageBox = this.innerElement.getBox();
        this.onDrag(e);
    },
    onTouchEnd: function(e) {
        this.onDragEnd();
    },
    
    onDragEnd: function() {
        this.innerElement.removeCls(this.getBaseCls() + '-pressed');
        
        if (this.indicator) {
            this.indicator.hide();
        }
    },
    
    onDrag: function(e) {
        var point = Ext.util.Point.fromEvent(e),
            target, isValidTarget,
            pageBox = this.pageBox;
        if (!pageBox) {
            pageBox = this.pageBox = this.el.getBox();
        }
        if (this.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.getParent() == this.element;
            
            if (this.indicator) {
                this.indicator.show();
                var halfIndicatorHeight = this.indicator.getHeight() / 2,
                    y = point.y - this.element.getY();
                y = Math.min(Math.max(y, halfIndicatorHeight), this.element.getHeight() - halfIndicatorHeight);
                if (this.indicatorInner && isValidTarget) {
                    this.indicatorInner.setHtml(target.getHtml().toUpperCase());
                }
                this.indicator.setTop(y - (halfIndicatorHeight));
            }
        } else {
            if (point.x > pageBox.right || point.x < pageBox.left) {
                return;
            }
            target = Ext.Element.fromPoint(point.x, pageBox.top + (pageBox.height / 2));
            isValidTarget = target.getParent() == this.element;
        }
        if (target && isValidTarget) {
            this.fireEvent('index', this, target.dom.innerHTML, target);
        }
    },
    destroy: function() {
        var me = this,
            elements = Array.prototype.slice.call(me.innerElement.dom.childNodes),
            ln = elements.length,
            i = 0;
        for (; i < ln; i++) {
            Ext.removeNode(elements[i]);
        }
        this.callParent();
    }
});


Ext.define('Ext.dataview.ListItemHeader', {
    extend: Ext.Component,
    xtype: 'listitemheader',
    config: {
        
        baseCls: Ext.baseCSSPrefix + 'list-header'
    }
});


Ext.define('Ext.dataview.component.ListItem', {
    extend: Ext.dataview.component.DataItem,
    xtype: 'listitem',
    config: {
        baseCls: Ext.baseCSSPrefix + 'list-item',
        dataMap: null,
        body: {
            xtype: 'component',
            cls: 'x-list-item-body'
        },
        disclosure: {
            xtype: 'component',
            cls: 'x-list-disclosure',
            hidden: true,
            docked: 'right'
        },
        header: {
            xtype: 'component',
            cls: 'x-list-header',
            html: ' '
        },
        tpl: null,
        items: null
    },
    applyBody: function(body) {
        if (body && !body.isComponent) {
            body = Ext.factory(body, Ext.Component, this.getBody());
        }
        return body;
    },
    updateBody: function(body, oldBody) {
        if (body) {
            this.add(body);
        } else if (oldBody) {
            oldBody.destroy();
        }
    },
    applyHeader: function(header) {
        if (header && !header.isComponent) {
            header = Ext.factory(header, Ext.Component, this.getHeader());
        }
        return header;
    },
    updateHeader: function(header, oldHeader) {
        if (oldHeader) {
            oldHeader.destroy();
        }
    },
    applyDisclosure: function(disclosure) {
        if (disclosure && !disclosure.isComponent) {
            disclosure = Ext.factory(disclosure, Ext.Component, this.getDisclosure());
        }
        return disclosure;
    },
    updateDisclosure: function(disclosure, oldDisclosure) {
        if (disclosure) {
            this.add(disclosure);
        } else if (oldDisclosure) {
            oldDisclosure.destroy();
        }
    },
    updateTpl: function(tpl) {
        this.getBody().setTpl(tpl);
    },
    updateRecord: function(record) {
        var me = this,
            dataview = me.dataview || this.getDataview(),
            data = record && dataview.prepareData(record.getData(true), dataview.getStore().indexOf(record), record),
            dataMap = me.getDataMap(),
            body = this.getBody(),
            disclosure = this.getDisclosure();
        me._record = record;
        if (dataMap) {
            me.doMapData(dataMap, data, body);
        } else if (body) {
            body.updateData(data || null);
        }
        if (disclosure && record && dataview.getOnItemDisclosure()) {
            var disclosureProperty = dataview.getDisclosureProperty();
            disclosure[(data.hasOwnProperty(disclosureProperty) && data[disclosureProperty] === false) ? 'hide' : 'show']();
        }
        
        me.fireEvent('updatedata', me, data);
    },
    destroy: function() {
        Ext.destroy(this.getHeader());
        this.callParent();
    }
});


Ext.define('Ext.dataview.component.SimpleListItem', {
    extend: Ext.Component,
    xtype: 'simplelistitem',
    config: {
        baseCls: Ext.baseCSSPrefix + 'list-item',
        disclosure: {
            xtype: 'component',
            cls: 'x-list-disclosure',
            hidden: true
        },
        header: {
            xtype: 'component',
            cls: 'x-list-header',
            html: ' '
        },
        
        dataview: null,
        
        record: null
    },
    initialize: function() {
        this.element.addCls(this.getBaseCls() + '-tpl');
    },
    applyHeader: function(header) {
        if (header && !header.isComponent) {
            header = Ext.factory(header, Ext.Component, this.getHeader());
        }
        return header;
    },
    updateHeader: function(header, oldHeader) {
        if (oldHeader) {
            oldHeader.destroy();
        }
    },
    applyDisclosure: function(disclosure) {
        if (disclosure && !disclosure.isComponent) {
            disclosure = Ext.factory(disclosure, Ext.Component, this.getDisclosure());
        }
        return disclosure;
    },
    updateDisclosure: function(disclosure, oldDisclosure) {
        if (disclosure) {
            this.element.appendChild(disclosure.renderElement);
        } else if (oldDisclosure) {
            oldDisclosure.destroy();
        }
    },
    updateRecord: function(record) {
        var me = this,
            dataview = me.dataview || this.getDataview(),
            data = record && dataview.prepareData(record.getData(true), dataview.getStore().indexOf(record), record),
            disclosure = this.getDisclosure();
        me.updateData(data || null);
        if (disclosure && record && dataview.getOnItemDisclosure()) {
            var disclosureProperty = dataview.getDisclosureProperty();
            disclosure[(data.hasOwnProperty(disclosureProperty) && data[disclosureProperty] === false) ? 'hide' : 'show']();
        }
    },
    destroy: function() {
        Ext.destroy(this.getHeader(), this.getDisclosure());
        this.callParent();
    }
});


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;
            this.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 adjustments = this.adjustments,
            indices = adjustments.indices,
            heights = adjustments.heights,
            map = this.map,
            ln = indices.length,
            minimumHeight = this.getMinimumHeight(),
            difference = 0,
            i, j, height, index, nextIndex, currentHeight;
        if (!adjustments.indices.length) {
            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;
            }
        }
        this.offset += difference;
        this.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.dataview.List', {
    alternateClassName: 'Ext.List',
    extend: Ext.dataview.DataView,
    xtype: 'list',
    mixins: [
        Ext.mixin.Hookable
    ],
    
    config: {
        
        container: {
            lazy: true,
            $value: {
                xtype: 'container',
                scrollable: {}
            }
        },
        
        layout: 'fit',
        
        indexBar: false,
        icon: null,
        
        preventSelectionOnDisclose: true,
        
        baseCls: Ext.baseCSSPrefix + 'list',
        
        pinHeaders: true,
        
        grouped: null,
        
        onItemDisclosure: null,
        
        disclosureProperty: 'disclosure',
        
        
        
        
        defaultType: undefined,
        
        itemMap: {},
        
        itemHeight: null,
        
        variableHeights: false,
        
        refreshHeightOnUpdate: true,
        
        infinite: false,
        
        useSimpleItems: true,
        
        scrollable: null,
        
        bufferSize: 20,
        minimumBufferDistance: 5,
        
        striped: false
    },
    topRenderedIndex: 0,
    topVisibleIndex: 0,
    visibleCount: 0,
    
    constructor: function(config) {
        this.callParent([
            config
        ]);
        var layout = this.getLayout();
        if (layout && !layout.isFit) {
            Ext.Logger.error('The base layout for a DataView must always be a Fit Layout');
        }
    },
    
    
    beforeInitialize: function() {
        var me = this,
            container = me.container,
            baseCls = me.getBaseCls(),
            scrollViewElement, pinnedHeader;
        Ext.apply(me, {
            listItems: [],
            headerItems: [],
            updatedItems: [],
            headerMap: [],
            recordMap: {},
            scrollDockItems: {
                top: [],
                bottom: []
            }
        });
        me.translationMethod = 'csstransform';
        
        if (!container) {
            container = me.container = me.createContainer();
        }
        
        
        
        me.add(container);
        
        scrollViewElement = me.scrollViewElement = container.bodyElement;
        me.scrollElement = container.innerElement;
        
        
        pinnedHeader = me.pinnedHeader = Ext.factory({
            xtype: 'listitemheader',
            html: '&nbsp;',
            translatable: {
                translationMethod: me.translationMethod
            },
            cls: [
                baseCls + '-header',
                baseCls + '-header-swap'
            ]
        });
        pinnedHeader.translate(0, -10000);
        pinnedHeader.$position = -10000;
        scrollViewElement.insertFirst(pinnedHeader.renderElement);
        container.getScrollable().on({
            scroll: 'onScroll',
            refresh: 'onScrollerRefresh',
            scope: me
        });
    },
    
    createContainer: function() {
        var config = Ext.merge({
                scrollable: {
                    autoRefresh: this.getInfinite() ? null : true
                }
            }, this.getContainer());
        return Ext.create(config);
    },
    getScrollable: function() {
        return this.container.getScrollable();
    },
    
    initialize: function() {
        var me = this,
            container = me.container,
            scrollViewElement = me.scrollViewElement,
            indexBar = me.getIndexBar(),
            triggerEvent = me.getTriggerEvent(),
            triggerCtEvent = me.getTriggerCtEvent();
        if (indexBar) {
            scrollViewElement.appendChild(indexBar.renderElement);
        }
        if (triggerEvent) {
            me.on(triggerEvent, me.onItemTrigger, me);
        }
        if (triggerCtEvent) {
            me.on(triggerCtEvent, me.onContainerTrigger, me);
        }
        container.element.on({
            delegate: '.' + me.getBaseCls() + '-disclosure',
            tap: 'handleItemDisclosure',
            scope: me
        });
        container.element.on({
            resize: 'onContainerResize',
            scope: me
        });
        
        container.innerElement.on({
            touchstart: 'onItemTouchStart',
            touchend: 'onItemTouchEnd',
            tap: 'onItemTap',
            taphold: 'onItemTapHold',
            singletap: 'onItemSingleTap',
            doubletap: 'onItemDoubleTap',
            swipe: 'onItemSwipe',
            delegate: '.' + Ext.baseCSSPrefix + 'list-item',
            scope: me
        });
        if (me.getStore()) {
            if (me.isPainted()) {
                me.refresh();
            } else {
                me.on({
                    painted: 'refresh',
                    single: true
                });
            }
        }
    },
    getRefItems: function(deep) {
        var result = [],
            candidates = this.callParent([
                deep
            ]),
            len = candidates.length,
            i, candidate;
        
        
        for (i = 0; i < len; i++) {
            candidate = candidates[i];
            if (!candidate.hasOwnProperty('$position') || candidate.$position > -1) {
                result[result.length] = candidate;
            }
        }
        return result;
    },
    onScroll: function(scroller, x, y) {
        var me = this,
            pinnedHeader = me.pinnedHeader,
            store = me.getStore(),
            storeCount = store && store.getCount(),
            grouped = me.isGrouping(),
            infinite = me.getInfinite();
        
        
        
        
        if (x) {
            x = -x;
        }
        if (y) {
            y = -y;
        }
        if (!storeCount) {
            me.showEmptyText();
            me.showEmptyScrollDock();
            pinnedHeader.$position = -10000;
            pinnedHeader.translate(0, -10000);
        } else if (infinite && me.itemsCount) {
            me.handleItemUpdates(y);
            me.handleItemHeights();
            me.handleItemTransforms();
            if (!me.onIdleBound) {
                Ext.AnimationQueue.onIdle(me.onAnimationIdle, me);
                me.onIdleBound = true;
            }
        }
        if (grouped && me.groups && me.groups.length && me.getPinHeaders()) {
            me.handlePinnedHeader(y);
        }
        
        me.onScrollBinder(x, y);
    },
    onScrollerRefresh: function(scroller) {
        var position = scroller.getPosition();
        this.onScroll(scroller, position.x, position.y);
    },
    onScrollBinder: function() {},
    handleItemUpdates: function(y) {
        var me = this,
            listItems = me.listItems,
            itemsCount = listItems.length,
            info = me.getListItemInfo(),
            itemMap = me.getItemMap(),
            bufferSize = me.getBufferSize(),
            lastIndex = me.getStore().getCount() - 1,
            minimumBufferDistance = me.getMinimumBufferDistance(),
            currentTopVisibleIndex = me.topVisibleIndex,
            topRenderedIndex = me.topRenderedIndex,
            updateCount, i, item, topVisibleIndex, bufferDistance, itemIndex;
        
        me.topVisibleIndex = topVisibleIndex = Math.max(0, itemMap.findIndex(-y) || 0);
        if (currentTopVisibleIndex !== topVisibleIndex) {
            
            if (currentTopVisibleIndex > topVisibleIndex) {
                bufferDistance = topVisibleIndex - topRenderedIndex;
                if (bufferDistance < minimumBufferDistance) {
                    updateCount = Math.min(itemsCount, minimumBufferDistance - bufferDistance);
                    if (updateCount == itemsCount) {
                        me.topRenderedIndex = topRenderedIndex = Math.max(0, topVisibleIndex - (bufferSize - minimumBufferDistance));
                        
                        for (i = 0; i < updateCount; i++) {
                            itemIndex = topRenderedIndex + i;
                            item = listItems[i];
                            me.updateListItem(item, itemIndex, info);
                        }
                    } else {
                        for (i = 0; i < updateCount; i++) {
                            itemIndex = topRenderedIndex - i - 1;
                            if (itemIndex < 0) {
                                break;
                            }
                            item = listItems.pop();
                            listItems.unshift(item);
                            me.updateListItem(item, itemIndex, info);
                            me.topRenderedIndex--;
                        }
                    }
                }
            } else 
            {
                bufferDistance = bufferSize - (topVisibleIndex - topRenderedIndex);
                if (bufferDistance < minimumBufferDistance) {
                    updateCount = Math.min(itemsCount, minimumBufferDistance - bufferDistance);
                    if (updateCount == itemsCount) {
                        me.topRenderedIndex = topRenderedIndex = Math.min(lastIndex - itemsCount, topVisibleIndex - minimumBufferDistance);
                        
                        for (i = 0; i < updateCount; i++) {
                            itemIndex = topRenderedIndex + i;
                            item = listItems[i];
                            me.updateListItem(item, itemIndex, info);
                        }
                    } else {
                        for (i = 0; i < updateCount; i++) {
                            itemIndex = topRenderedIndex + itemsCount + i;
                            if (itemIndex > lastIndex) {
                                break;
                            }
                            item = listItems.shift();
                            listItems.push(item);
                            me.updateListItem(item, itemIndex, info);
                            me.topRenderedIndex++;
                        }
                    }
                }
            }
        }
    },
    onAnimationIdle: function() {
        var me = this,
            info = me.getListItemInfo(),
            bufferSize = me.getBufferSize(),
            topVisibleIndex = me.topVisibleIndex,
            topRenderedIndex = me.topRenderedIndex,
            lastIndex = me.getStore().getCount() - 1,
            listItems = me.listItems,
            itemsCount = listItems.length,
            topBufferDistance, bottomBufferDistance, i, ln, item, itemIndex;
        topBufferDistance = topVisibleIndex - topRenderedIndex;
        bottomBufferDistance = topRenderedIndex + bufferSize - topVisibleIndex;
        if (topBufferDistance < bottomBufferDistance) {
            
            
            
            if (topVisibleIndex > 0) {
                ln = bottomBufferDistance - topBufferDistance;
                for (i = 0; i < ln; i++) {
                    itemIndex = topRenderedIndex - i - 1;
                    if (itemIndex < 0) {
                        break;
                    }
                    item = listItems.pop();
                    listItems.unshift(item);
                    me.updateListItem(item, itemIndex, info);
                    me.topRenderedIndex--;
                }
            }
        } else {
            ln = topBufferDistance - bottomBufferDistance;
            for (i = 0; i < ln; i++) {
                itemIndex = topRenderedIndex + itemsCount + i;
                if (itemIndex > lastIndex) {
                    break;
                }
                item = listItems.shift();
                listItems.push(item);
                me.updateListItem(item, itemIndex, info);
                me.topRenderedIndex++;
            }
        }
        me.handleItemHeights();
        me.handleItemTransforms();
        me.onIdleBound = false;
    },
    handleItemHeights: function() {
        var me = this,
            updatedItems = me.updatedItems,
            ln = updatedItems.length,
            itemMap = me.getItemMap(),
            useSimpleItems = me.getUseSimpleItems(),
            minimumHeight = itemMap.getMinimumHeight(),
            headerIndices = me.headerIndices,
            headerMap = me.headerMap,
            variableHeights = me.getVariableHeights(),
            itemIndex, i, j, jln, item, height, scrollDockHeight;
        for (i = 0; i < ln; i++) {
            item = updatedItems[i];
            itemIndex = item.$dataIndex;
            
            if (itemIndex !== null) {
                if (variableHeights) {
                    height = useSimpleItems ? item.element.getHeight() : item.element.getFirstChild().getHeight();
                    height = Math.max(height, minimumHeight);
                } else {
                    height = minimumHeight;
                }
                item.$ownItemHeight = height;
                jln = me.scrollDockItems.top.length;
                if (item.isFirst) {
                    me.totalScrollDockTopHeight = 0;
                    for (j = 0; j < jln; j++) {
                        scrollDockHeight = me.scrollDockItems.top[j].$scrollDockHeight;
                        height += scrollDockHeight;
                        me.totalScrollDockTopHeight += scrollDockHeight;
                    }
                }
                jln = me.scrollDockItems.bottom.length;
                if (item.isLast) {
                    for (j = 0; j < jln; j++) {
                        scrollDockHeight = me.scrollDockItems.bottom[j].$scrollDockHeight;
                        height += scrollDockHeight;
                    }
                }
                if (headerIndices && headerIndices[itemIndex]) {
                    height += me.headerHeight;
                }
                itemMap.setItemHeight(itemIndex, height);
                item.$height = height;
            }
        }
        itemMap.update();
        headerMap.length = 0;
        for (i in headerIndices) {
            if (headerIndices.hasOwnProperty(i)) {
                headerMap.push(itemMap.map[i]);
            }
        }
        me.updatedItems.length = 0;
        me.refreshScroller(true);
    },
    handleItemTransforms: function() {
        var me = this,
            listItems = me.listItems,
            itemsCount = listItems.length,
            itemMap = me.getItemMap(),
            scrollDockItems = me.scrollDockItems,
            grouped = me.isGrouping(),
            item, transY, i, jln, j;
        for (i = 0; i < itemsCount; i++) {
            item = listItems[i];
            transY = itemMap.map[item.$dataIndex];
            if (!item.$hidden && item.$position !== transY) {
                item.$position = transY;
                jln = scrollDockItems.top.length;
                if (item.isFirst && jln) {
                    for (j = 0; j < jln; j++) {
                        scrollDockItems.top[j].translate(0, transY);
                        transY += scrollDockItems.top[j].$scrollDockHeight;
                    }
                }
                if (grouped && me.headerIndices && me.headerIndices[item.$dataIndex]) {
                    item.getHeader().translate(0, transY);
                    transY += me.headerHeight;
                }
                item.translate(0, transY);
                transY += item.$ownItemHeight;
                jln = scrollDockItems.bottom.length;
                if (item.isLast && jln) {
                    for (j = 0; j < jln; j++) {
                        scrollDockItems.bottom[j].translate(0, transY);
                        transY += scrollDockItems.bottom[j].$scrollDockHeight;
                    }
                }
            }
        }
    },
    handlePinnedHeader: function(y) {
        var me = this,
            pinnedHeader = me.pinnedHeader,
            itemMap = me.getItemMap(),
            groups = me.groups,
            headerMap = me.headerMap,
            headerHeight = me.headerHeight,
            store = me.getStore(),
            totalScrollDockTopHeight = me.totalScrollDockTopHeight,
            record, closestHeader, pushedHeader, transY, headerString;
        closestHeader = itemMap.binarySearch(headerMap, -y);
        record = groups.getAt(closestHeader).getAt(0);
        if (record) {
            pushedHeader = y + headerMap[closestHeader + 1] - headerHeight;
            
            if (y >= 0 || (closestHeader === 0 && totalScrollDockTopHeight + y >= 0) || (closestHeader === 0 && -y <= headerMap[closestHeader])) {
                transY = -10000;
            }
            
            else if (pushedHeader < 0) {
                transY = pushedHeader;
            } else 
            {
                transY = Math.max(0, y);
            }
            headerString = store.getGrouper().getGroupString(record);
            if (pinnedHeader.$currentHeader != headerString) {
                pinnedHeader.setHtml(headerString);
                pinnedHeader.$currentHeader = headerString;
            }
            if (pinnedHeader.$position != transY) {
                pinnedHeader.translate(0, transY);
                pinnedHeader.$position = transY;
            }
        }
    },
    createItem: function(config) {
        var me = this,
            container = me.container,
            listItems = me.listItems,
            infinite = me.getInfinite(),
            scrollElement = me.scrollElement,
            item, header, itemCls;
        config.$initParent = me;
        item = Ext.factory(config);
        delete config.$initParent;
        item.dataview = me;
        item.$height = config.minHeight;
        if (!infinite) {
            itemCls = me.getBaseCls() + '-item-relative';
            item.addCls(itemCls);
        }
        header = item.getHeader && item.getHeader();
        if (header) {
            if (!infinite) {
                header.addCls(itemCls);
            } else {
                header.setTranslatable({
                    translationMethod: this.translationMethod
                });
                header.translate(0, -10000);
                scrollElement.insertFirst(header.renderElement);
            }
        }
        container.doAdd(item);
        listItems.push(item);
        return item;
    },
    setItemsCount: function(itemsCount, itemConfig) {
        var me = this,
            listItems = me.listItems,
            config = itemConfig || me.getListItemConfig(),
            difference = itemsCount - listItems.length,
            i;
        
        for (i = 0; i < difference; i++) {
            me.createItem(config);
        }
        
        for (i = difference; i < 0; i++) {
            listItems.pop().destroy();
        }
        me.itemsCount = itemsCount;
        
        me.updateAllListItems();
        
        if (Ext.browser.is.AndroidStock && me.container.element && itemsCount === 0 && difference !== 0) {
            me.container.element.redraw();
        }
        return me.listItems;
    },
    updateListItem: function(item, index, info) {
        var me = this,
            recordMap = me.recordMap,
            oldRecord = item.getRecord(),
            store = info.store,
            record = store.getAt(index),
            headerIndices = me.headerIndices,
            footerIndices = me.footerIndices,
            header = item.getHeader && item.getHeader(),
            scrollDockItems = me.scrollDockItems,
            updatedItems = me.updatedItems,
            infinite = me.getInfinite(),
            storeCount = store.getCount(),
            grouper = store.getGrouper(),
            itemCls = [],
            headerCls = [],
            itemRemoveCls = [
                info.headerCls,
                info.footerCls,
                info.firstCls,
                info.lastCls,
                info.selectedCls,
                info.stripeCls
            ],
            headerRemoveCls = [
                info.headerCls,
                info.footerCls,
                info.firstCls,
                info.lastCls
            ],
            ln, i, scrollDockItem, viewModel;
        
        
        
        if (infinite) {
            item.$position = -10000;
        }
        
        if (!record) {
            item.setRecord(null);
            if (oldRecord) {
                delete recordMap[oldRecord.internalId];
            }
            if (infinite) {
                item.translate(0, -10000);
            } else {
                item.hide();
            }
            if (header) {
                if (infinite) {
                    header.translate(0, -10000);
                } else {
                    header.hide();
                }
            }
            item.$hidden = true;
            return;
        } else if (item.$hidden) {
            if (!infinite) {
                item.show();
            }
            item.$hidden = false;
        }
        if (infinite) {
            updatedItems.push(item);
        }
        
        
        if (item.isFirst && index !== 0) {
            ln = scrollDockItems.top.length;
            for (i = 0; i < ln; i++) {
                scrollDockItem = scrollDockItems.top[i];
                if (infinite) {
                    scrollDockItem.translate(0, -10000);
                }
            }
            item.isFirst = false;
        }
        
        
        if (item.isLast && index !== storeCount - 1) {
            ln = scrollDockItems.bottom.length;
            for (i = 0; i < ln; i++) {
                scrollDockItem = scrollDockItems.bottom[i];
                if (infinite) {
                    scrollDockItem.translate(0, -10000);
                }
            }
            item.isLast = false;
        }
        
        if (item.$dataIndex !== index) {
            item.$dataIndex = index;
            me.fireEvent('itemindexchange', me, record, index, item);
        }
        
        if (oldRecord === record) {
            item.updateRecord(record);
        } else {
            if (oldRecord) {
                delete recordMap[oldRecord.internalId];
            }
            recordMap[record.internalId] = item;
            item.setRecord(record);
            viewModel = item.getViewModel();
            if (viewModel) {
                viewModel.set('record', record);
            }
        }
        if (me.isSelected(record)) {
            itemCls.push(info.selectedCls);
        }
        if (info.grouped) {
            if (headerIndices[index]) {
                itemCls.push(info.headerCls);
                headerCls.push(info.headerCls);
                header.setHtml(grouper.getGroupString(record));
                if (!infinite) {
                    header.renderElement.insertBefore(item.renderElement);
                }
                header.show();
            } else {
                if (infinite) {
                    header.translate(0, -10000);
                } else {
                    header.hide();
                }
            }
            if (footerIndices[index]) {
                itemCls.push(info.footerCls);
                headerCls.push(info.footerCls);
            }
        }
        if (header && !info.grouped) {
            if (infinite) {
                header.translate(0, -10000);
            } else {
                header.hide();
            }
        }
        if (index === 0) {
            item.isFirst = true;
            itemCls.push(info.firstCls);
            headerCls.push(info.firstCls);
            if (!info.grouped) {
                itemCls.push(info.headerCls);
                headerCls.push(info.headerCls);
            }
            if (!infinite) {
                for (i = 0 , ln = scrollDockItems.top.length; i < ln; i++) {
                    scrollDockItem = scrollDockItems.top[i];
                    if (info.grouped) {
                        scrollDockItem.renderElement.insertBefore(header.renderElement);
                    } else {
                        scrollDockItem.renderElement.insertBefore(item.renderElement);
                    }
                }
            }
        }
        if (index === storeCount - 1) {
            item.isLast = true;
            itemCls.push(info.lastCls);
            headerCls.push(info.lastCls);
            if (!info.grouped) {
                itemCls.push(info.footerCls);
                headerCls.push(info.footerCls);
            }
            if (!infinite) {
                for (i = 0 , ln = scrollDockItems.bottom.length; i < ln; i++) {
                    scrollDockItem = scrollDockItems.bottom[i];
                    scrollDockItem.renderElement.insertAfter(item.renderElement);
                }
            }
        }
        if (info.striped && index % 2 === 1) {
            itemCls.push(info.stripeCls);
        }
        item.renderElement.replaceCls(itemRemoveCls, itemCls);
        if (header) {
            header.renderElement.replaceCls(headerRemoveCls, headerCls);
        }
    },
    updateAllListItems: function() {
        var me = this,
            store, items, info, topRenderedIndex, i, ln;
        if (!me.initialized) {
            return;
        }
        store = me.getStore();
        items = me.listItems;
        info = me.getListItemInfo();
        topRenderedIndex = me.topRenderedIndex;
        if (store) {
            for (i = 0 , ln = items.length; i < ln; i++) {
                me.updateListItem(items[i], topRenderedIndex + i, info);
            }
        }
        if (me.isPainted()) {
            if (me.getInfinite() && store && store.getCount()) {
                me.handleItemHeights();
            }
            me.refreshScroller();
        }
    },
    doRefresh: function() {
        var me = this,
            infinite = me.getInfinite(),
            scroller = me.container.getScrollable(),
            storeCount = me.getStore().getCount();
        if (infinite) {
            me.getItemMap().populate(storeCount, this.topRenderedIndex);
        }
        if (me.getGrouped()) {
            me.refreshHeaderIndices();
        }
        
        if (storeCount) {
            me.hideScrollDockItems();
            me.hideEmptyText();
            if (!infinite) {
                me.setItemsCount(storeCount);
                if (me.getScrollToTopOnRefresh()) {
                    scroller.scrollTo(0, 0);
                }
            } else {
                if (me.getScrollToTopOnRefresh()) {
                    me.topRenderedIndex = 0;
                    me.topVisibleIndex = 0;
                    scroller.scrollTo(null, 0);
                }
                me.updateAllListItems();
            }
        } else {
            me.onStoreClear();
        }
    },
    updateStore: function(store, oldStore) {
        var me = this,
            container = me.container;
        me.callParent([
            store,
            oldStore
        ]);
        if (me._fireResizeOnNextLoad && me.hasLoadedStore) {
            me._fireResizeOnNextLoad = false;
            me.onContainerResize(container, {
                height: container.element.getHeight()
            });
        }
    },
    onLoad: function(store) {
        var me = this,
            container = me.container;
        me.callParent([
            store
        ]);
        if (me._fireResizeOnNextLoad) {
            me._fireResizeOnNextLoad = false;
            me.onContainerResize(container, {
                height: container.element.getHeight()
            });
        }
    },
    onContainerResize: function(container, size) {
        var me = this,
            store = me.getStore(),
            currentVisibleCount, newVisibleCount, minHeight, listItems, itemMap, itemConfig;
        if (!me.headerHeight) {
            me.headerHeight = parseInt(me.pinnedHeader.renderElement.getHeight(), 10);
        }
        if (me.getInfinite()) {
            itemMap = me.getItemMap();
            minHeight = itemMap.getMinimumHeight();
            if (!store || (!store.getCount() && !store.isLoaded())) {
                
                
                
                me._fireResizeOnNextLoad = true;
                return;
            }
            if (!minHeight) {
                listItems = me.listItems;
                
                
                
                
                if (!listItems.length) {
                    
                    
                    
                    
                    itemConfig = me.getListItemConfig();
                    me.createItem(itemConfig);
                    me.updateListItem(listItems[0], 0, me.getListItemInfo());
                    me.visibleCount++;
                }
                minHeight = listItems[0].element.getHeight();
                
                itemMap.setMinimumHeight(minHeight);
                me.getItemMap().populate(me.getStore().getCount(), me.topRenderedIndex);
            }
            currentVisibleCount = me.visibleCount;
            newVisibleCount = Math.ceil(size.height / minHeight);
            if (newVisibleCount != currentVisibleCount) {
                me.visibleCount = newVisibleCount;
                me.setItemsCount(newVisibleCount + me.getBufferSize(), itemConfig);
                
                me.fireEvent('updatevisiblecount', this, newVisibleCount, currentVisibleCount);
            }
        } else if (me.listItems.length && me.getGrouped() && me.getPinHeaders()) {
            
            
            me.updateHeaderMap();
        }
    },
    refreshScroller: function(skipOnRefresh) {
        var me = this,
            scroller = me.container.getScrollable(),
            infinite = me.getInfinite(),
            height, scrollSize;
        if (infinite) {
            height = me.getItemMap().getTotalHeight();
            scrollSize = scroller.getSize();
            if (height != scrollSize.y) {
                scroller.setSize({
                    
                    
                    
                    
                    x: scroller.isTouchScroller ? scrollSize.x : null,
                    y: height
                });
            }
            if (!skipOnRefresh) {
                me.onScrollerRefresh(scroller);
            }
        } else {
            if (me.getGrouped() && me.getPinHeaders()) {
                me.updateHeaderMap();
            }
            scroller.refresh();
        }
    },
    updateHeaderMap: function() {
        var me = this,
            headerMap = me.headerMap,
            headerIndices = me.headerIndices,
            header, i;
        headerMap.length = 0;
        for (i in headerIndices) {
            if (headerIndices.hasOwnProperty(i)) {
                header = me.getItemAt(i).getHeader();
                headerMap.push(header.renderElement.dom.offsetTop);
            }
        }
    },
    applyVariableHeights: function(value) {
        if (!this.getInfinite()) {
            return true;
        }
        return value;
    },
    applyDefaultType: function(defaultType) {
        if (!defaultType) {
            defaultType = this.getUseSimpleItems() ? 'simplelistitem' : 'listitem';
        }
        return defaultType;
    },
    applyItemMap: function(itemMap) {
        return Ext.factory(itemMap, Ext.util.PositionMap, this.getItemMap());
    },
    updateItemHeight: function(itemHeight) {
        this.getItemMap().setMinimumHeight(itemHeight);
    },
    applyIndexBar: function(indexBar) {
        return Ext.factory(indexBar, Ext.dataview.IndexBar, this.getIndexBar());
    },
    updatePinHeaders: function(pinnedHeaders) {
        if (this.isPainted()) {
            this.pinnedHeader.translate(0, pinnedHeaders ? this.pinnedHeader.$position : -10000);
        }
    },
    updateItemTpl: function(newTpl) {
        var me = this,
            listItems = me.listItems,
            ln = listItems.length || 0,
            i, listItem;
        for (i = 0; i < ln; i++) {
            listItem = listItems[i];
            listItem.setTpl(newTpl);
        }
        me.updateAllListItems();
    },
    updateItemCls: function(newCls, oldCls) {
        var items = this.listItems,
            ln = items.length,
            i, item;
        for (i = 0; i < ln; i++) {
            item = items[i];
            item.removeCls(oldCls);
            item.addCls(newCls);
        }
    },
    updateIndexBar: function(indexBar, oldIndexBar) {
        var me = this,
            scrollViewElement = me.scrollViewElement;
        if (oldIndexBar) {
            oldIndexBar.un({
                index: 'onIndex',
                scope: me
            });
            if (!indexBar) {
                me.element.removeCls(me.getBaseCls() + '-indexed');
            }
            if (scrollViewElement) {
                scrollViewElement.removeChild(oldIndexBar.renderElement);
            }
        }
        if (indexBar) {
            indexBar.on({
                index: 'onIndex',
                scope: me
            });
            if (!oldIndexBar) {
                me.element.addCls(me.getBaseCls() + '-indexed');
            }
            if (scrollViewElement) {
                scrollViewElement.appendChild(indexBar.renderElement);
            }
        }
    },
    updateGrouped: function(grouped) {
        if (this.initialized) {
            this.handleGroupChange();
        }
    },
    onStoreGroupChange: function() {
        if (this.initialized) {
            this.handleGroupChange();
        }
    },
    onStoreAdd: function() {
        this.doRefresh();
    },
    onStoreRemove: function() {
        this.doRefresh();
    },
    onStoreUpdate: function(store, record, type, modifiedFieldNames, info) {
        var me = this,
            index, item;
        if (me.getInfinite() || info.indexChanged) {
            me.doRefresh();
        } else {
            index = store.indexOf(record);
            item = me.listItems[index];
            if (item) {
                me.updateListItem(item, index, me.getListItemInfo());
            }
        }
    },
    onStoreClear: function() {
        var me = this,
            scroller = me.container.getScrollable(),
            infinite = me.getInfinite();
        if (me.pinnedHeader) {
            me.pinnedHeader.translate(0, -10000);
        }
        me.getItemMap().populate(0, 0);
        if (!infinite) {
            me.setItemsCount(0);
        } else {
            me.topRenderedIndex = 0;
            me.topVisibleIndex = 0;
            me.updateAllListItems();
        }
        scroller.scrollTo(null, 0);
        me.refreshScroller();
    },
    showEmptyScrollDock: function() {
        var me = this,
            infinite = me.getInfinite(),
            scrollDockItems = me.scrollDockItems,
            offset = 0,
            i, ln, item;
        for (i = 0 , ln = scrollDockItems.top.length; i < ln; i++) {
            item = scrollDockItems.top[i];
            if (infinite) {
                item.translate(0, offset);
                offset += item.$scrollDockHeight;
            } else {
                this.scrollElement.appendChild(item.renderElement);
            }
        }
        for (i = 0 , ln = scrollDockItems.bottom.length; i < ln; i++) {
            item = scrollDockItems.bottom[i];
            if (infinite) {
                item.translate(0, offset);
                offset += item.$scrollDockHeight;
            } else {
                this.scrollElement.appendChild(item.renderElement);
            }
        }
    },
    hideScrollDockItems: function() {
        var me = this,
            infinite = me.getInfinite(),
            scrollDockItems = me.scrollDockItems,
            i, ln, item;
        if (!infinite) {
            return;
        }
        for (i = 0 , ln = scrollDockItems.top.length; i < ln; i++) {
            item = scrollDockItems.top[i];
            item.translate(0, -10000);
        }
        for (i = 0 , ln = scrollDockItems.bottom.length; i < ln; i++) {
            item = scrollDockItems.bottom[i];
            item.translate(0, -10000);
        }
    },
    
    getItem: function(record) {
        var item;
        if (record) {
            item = this.recordMap[record.internalId];
        }
        return item || null;
    },
    
    getItemAt: function(index) {
        var listItems = this.listItems,
            ln = listItems.length,
            i, listItem;
        for (i = 0; i < ln; i++) {
            listItem = listItems[i];
            if (listItem.$dataIndex == index) {
                return listItem;
            }
        }
    },
    
    getItemIndex: function(item) {
        return item.$dataIndex;
    },
    
    getViewItems: function() {
        return this.listItems;
    },
    getListItemInfo: function() {
        var me = this,
            baseCls = me.getBaseCls();
        return {
            store: me.getStore(),
            grouped: me.isGrouping(),
            baseCls: baseCls,
            selectedCls: me.getSelectedCls(),
            headerCls: baseCls + '-header-wrap',
            footerCls: baseCls + '-footer-wrap',
            firstCls: baseCls + '-item-first',
            lastCls: baseCls + '-item-last',
            stripeCls: baseCls + '-item-odd',
            striped: me.getStriped(),
            itemMap: me.getItemMap(),
            defaultItemHeight: me.getItemHeight()
        };
    },
    getListItemConfig: function() {
        var me = this,
            minimumHeight = me.getItemMap().getMinimumHeight(),
            config = {
                xtype: me.getDefaultType(),
                tpl: me.getItemTpl(),
                minHeight: minimumHeight,
                cls: me.getItemCls()
            };
        if (me.getInfinite()) {
            config.translatable = {
                translationMethod: this.translationMethod
            };
        }
        if (!me.getVariableHeights()) {
            config.height = minimumHeight;
        }
        return Ext.merge(config, me.getItemConfig());
    },
    refreshHeaderIndices: function() {
        var me = this,
            store = me.getStore(),
            storeLn = store && store.getCount(),
            groups = store.getGrouper() ? store.getGroups() : null,
            grouped = me.getGrouped(),
            headerIndices = me.headerIndices = {},
            footerIndices = me.footerIndices = {},
            i, previousIndex, firstGroupedRecord, storeIndex, groupLn;
        if (!grouped || !groups) {
            return footerIndices;
        }
        groupLn = groups.length;
        me.groups = groups;
        for (i = 0; i < groupLn; i++) {
            firstGroupedRecord = groups.getAt(i).getAt(0);
            storeIndex = store.indexOf(firstGroupedRecord);
            headerIndices[storeIndex] = true;
            previousIndex = storeIndex - 1;
            if (previousIndex >= 0) {
                footerIndices[previousIndex] = true;
            }
        }
        footerIndices[storeLn - 1] = true;
        return headerIndices;
    },
    onIndex: function(indexBar, index) {
        var me = this,
            key = index.toLowerCase(),
            store = me.getStore(),
            groups = store.getGroups(),
            ln = groups.length,
            group, groupKey, i, closest;
        for (i = 0; i < ln; i++) {
            group = groups.getAt(i);
            groupKey = group.getGroupKey().toLowerCase();
            if (groupKey >= key) {
                closest = group;
                break;
            } else {
                closest = group;
            }
        }
        if (closest) {
            this.scrollToRecord(closest.getAt(0));
        }
    },
    
    scrollToRecord: function(record, animate, overscroll) {
        var me = this,
            scroller = me.container.getScrollable(),
            store = me.getStore(),
            index = store.indexOf(record),
            header;
        
        scroller.stopAnimation();
        
        var elementHeight = scroller.getElement().getHeight(),
            scrollHeight = scroller.getSize().y,
            maxOffset = scrollHeight - elementHeight,
            offset, item;
        if (me.getInfinite()) {
            offset = me.getItemMap().map[index];
        } else {
            item = me.listItems[index];
            header = item.getHeader && item.getHeader();
            if (header && header.isPainted()) {
                offset = header.renderElement.dom.offsetTop;
            } else {
                offset = item.renderElement.dom.offsetTop;
            }
        }
        if (!overscroll) {
            offset = Math.min(offset, maxOffset);
        }
        scroller.scrollTo(0, offset, !!animate);
    },
    onItemAdd: function(item) {
        var me = this,
            config = item.config;
        if (config.scrollDock) {
            if (config.scrollDock == 'bottom') {
                me.scrollDockItems.bottom.push(item);
            } else {
                me.scrollDockItems.top.push(item);
            }
            if (me.getInfinite()) {
                item.on({
                    resize: 'onScrollDockItemResize',
                    scope: this
                });
                item.addCls(me.getBaseCls() + '-scrolldockitem');
                item.setTranslatable({
                    translationMethod: this.translationMethod
                });
                item.translate(0, -10000);
                item.$scrollDockHeight = 0;
            }
            me.container.doAdd(item);
        } else {
            me.callParent(arguments);
        }
    },
    
    getScrollDockedItems: function() {
        return this.scrollDockItems.bottom.slice().concat(this.scrollDockItems.top.slice());
    },
    onScrollDockItemResize: function(dockItem, size) {
        var me = this,
            items = me.listItems,
            ln = items.length,
            i, item;
        Ext.getCmp(dockItem.id).$scrollDockHeight = size.height;
        for (i = 0; i < ln; i++) {
            item = items[i];
            if (item.isLast) {
                me.updatedItems.push(item);
                if (me.isPainted()) {
                    me.refreshScroller();
                }
                break;
            }
        }
    },
    onItemTouchStart: function(e) {
        this.container.innerElement.on({
            touchmove: 'onItemTouchMove',
            delegate: '.' + Ext.baseCSSPrefix + 'list-item',
            single: true,
            scope: this
        });
        this.callParent(this.parseEvent(e));
    },
    onItemTouchMove: function(e) {
        this.callParent(this.parseEvent(e));
    },
    onItemTouchEnd: function(e) {
        this.container.innerElement.un({
            touchmove: 'onItemTouchMove',
            delegate: '.' + Ext.baseCSSPrefix + 'list-item',
            scope: this
        });
        this.callParent(this.parseEvent(e));
    },
    onItemTap: function(e) {
        this.callParent(this.parseEvent(e));
    },
    onItemTapHold: function(e) {
        this.callParent(this.parseEvent(e));
    },
    onItemSingleTap: function(e) {
        this.callParent(this.parseEvent(e));
    },
    onItemDoubleTap: function(e) {
        this.callParent(this.parseEvent(e));
    },
    onItemSwipe: function(e) {
        this.callParent(this.parseEvent(e));
    },
    parseEvent: function(e) {
        var me = this,
            target = Ext.fly(e.currentTarget).findParent('.' + Ext.baseCSSPrefix + 'list-item', 8),
            item = Ext.getCmp(target.id);
        return [
            me,
            item,
            item.$dataIndex,
            e
        ];
    },
    applyOnItemDisclosure: function(config) {
        if (Ext.isFunction(config)) {
            return {
                scope: this,
                handler: config
            };
        }
        return config;
    },
    handleItemDisclosure: function(e) {
        var me = this,
            item = Ext.getCmp(Ext.get(e.currentTarget).up('.x-list-item').id),
            index = item.$dataIndex,
            record = me.getStore().getAt(index);
        me.fireAction('disclose', [
            me,
            record,
            item,
            index,
            e
        ], 'doDisclose');
    },
    doDisclose: function(me, record, item, index, e) {
        var onItemDisclosure = me.getOnItemDisclosure();
        if (onItemDisclosure && onItemDisclosure.handler) {
            onItemDisclosure.handler.call(onItemDisclosure.scope || me, record, item, index, e);
        }
    },
    
    onItemTrigger: function(me, index, target, record, e) {
        if (!(this.getPreventSelectionOnDisclose() && Ext.fly(e.target).hasCls(this.getBaseCls() + '-disclosure'))) {
            this.callParent(arguments);
        }
    },
    destroy: function() {
        var me = this,
            items = me.listItems,
            ln = items.length,
            i;
        if (me.pinnedHeader) {
            me.pinnedHeader.destroy();
            me.pinnedHeader = null;
        }
        me.callParent();
        if (me.onIdleBound) {
            Ext.AnimationQueue.unIdle(me.onAnimationIdle, me);
        }
        for (i = 0; i < ln; i++) {
            items[i].destroy();
        }
        me.recordMap = me.listItems = null;
    },
    privates: {
        handleGroupChange: function() {
            var me = this,
                grouped = me.isGrouping(),
                baseCls = this.getBaseCls(),
                infinite = me.getInfinite(),
                pinnedHeader = me.pinnedHeader,
                cls = baseCls + '-grouped',
                unCls = baseCls + '-ungrouped';
            if (pinnedHeader) {
                pinnedHeader.translate(0, -10000);
            }
            if (grouped) {
                me.addCls(cls);
                me.removeCls(unCls);
            } else {
                me.addCls(unCls);
                me.removeCls(cls);
            }
            if (infinite) {
                me.refreshHeaderIndices();
                me.handleItemHeights();
            }
            me.updateAllListItems();
            if (infinite) {
                me.handleItemTransforms();
            }
        },
        isGrouping: function() {
            return Boolean(this.getGrouped() && this.getStore().getGrouper());
        }
    }
});


Ext.define('Ext.dataview.NestedList', {
    alternateClassName: 'Ext.NestedList',
    extend: Ext.Container,
    xtype: 'nestedlist',
    config: {
        
        baseCls: Ext.baseCSSPrefix + 'nested-list',
        
        
        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: {
            ui: 'back',
            hidden: true
        },
        
        listConfig: null,
        
        useSimpleItems: true,
        
        itemHeight: null,
        
        variableHeights: false,
        
        lastNode: null,
        
        lastActiveList: null,
        ui: null,
        clearSelectionOnListChange: true
    },
    
    
    
    
    
    
    
    
    
    
    constructor: function(config) {
        if (Ext.isObject(config)) {
            if (config.getTitleTextTpl) {
                this.getTitleTextTpl = config.getTitleTextTpl;
            }
            if (config.getItemTextTpl) {
                this.getItemTextTpl = config.getItemTextTpl;
            }
        }
        this.callParent([
            config
        ]);
    },
    onItemInteraction: 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;
    },
    
    onItemTap: function(list, index, target, record, e) {
        var me = this,
            store = list.getStore(),
            node = store.getAt(index);
        me.fireEvent('itemtap', this, list, index, target, record, e);
        if (node.isLeaf()) {
            me.fireEvent('leafitemtap', this, list, index, target, record, e);
            me.goToLeaf(node);
        } else {
            this.goToNode(node);
        }
    },
    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)));
    },
    onSelectionChange: function() {
        this.fireEvent.apply(this, [].concat('selectionchange', this, Array.prototype.slice.call(arguments)));
    },
    onItemDoubleTap: function() {
        this.fireEvent.apply(this, [].concat('itemdoubletap', this, Array.prototype.slice.call(arguments)));
    },
    onStoreBeforeLoad: function() {
        var loadingText = this.getLoadingText(),
            scroller = this.getScrollable();
        if (loadingText) {
            this.setMasked({
                xtype: 'loadmask',
                message: loadingText
            });
            
            if (scroller) {
                scroller.setDisabled(true);
            }
        }
        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');
    },
    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) {
        return Ext.factory(config, Ext.Button, this.getBackButton());
    },
    updateBackButton: function(newButton, oldButton) {
        if (newButton) {
            var me = this,
                toolbar;
            newButton.on('tap', me.onBackTap, me);
            newButton.setText(me.getBackText());
            toolbar = me.getToolbar();
            if (me.$backButtonContainer) {
                me.$backButtonContainer.insert(0, newButton);
            } else {
                toolbar.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,
            toolbar = me.getToolbar();
        if (toolbar && me.getUpdateTitleText()) {
            toolbar.setTitle(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.getInitialConfig('title');
            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.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();
        
        if (backButton) {
            var toolbar = me.getToolbar(),
                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.setTitle(me.renderTitleText(node));
        }
    },
    updateBackText: function(newText) {
        this.getBackButton().setText(newText);
    },
    
    goToNodeReverseAnimation: function(node) {
        var lastNode = this.getLastNode();
        if (!lastNode) {
            return false;
        }
        return (!lastNode.contains(node) && lastNode.isAncestor(node)) ? true : false;
    },
    
    getList: function(node) {
        var me = this,
            treeStore = new Ext.data.NodeStore({
                recursive: false,
                node: node,
                rootVisible: false,
                model: me.getStore().getModel(),
                proxy: 'memory'
            });
        node.expand();
        return Ext.Object.merge({
            xtype: 'list',
            useSimpleItems: me.getUseSimpleItems(),
            pressedDelay: 250,
            autoDestroy: true,
            store: treeStore,
            onItemDisclosure: me.getOnItemDisclosure(),
            allowDeselect: me.getAllowDeselect(),
            itemHeight: me.getItemHeight(),
            variableHeights: me.getVariableHeights(),
            emptyText: me.getEmptyText(),
            listeners: [
                {
                    itemdoubletap: 'onItemDoubleTap',
                    itemtap: 'onItemTap',
                    beforeselectionchange: 'onBeforeSelect',
                    containertap: 'onContainerTap',
                    scope: me
                },
                {
                    selectionchange: 'onSelectionChange',
                    itemtouchstart: 'onItemInteraction',
                    itemtap: 'onItemInteraction',
                    order: 'before',
                    scope: me
                }
            ],
            itemTpl: '<span<tpl if="leaf == true"> class="x-list-item-leaf"</tpl>>' + me.getItemTextTpl(node) + '</span>'
        }, me.getListConfig());
    }
});


Ext.define('Ext.dataview.element.List', {
    extend: Ext.dataview.element.Container,
    updateBaseCls: function(newBaseCls) {
        var me = this;
        me.itemClsShortCache = newBaseCls + '-item';
        me.headerClsShortCache = newBaseCls + '-header';
        me.headerClsCache = '.' + me.headerClsShortCache;
        me.headerItemClsShortCache = newBaseCls + '-header-item';
        me.footerClsShortCache = newBaseCls + '-footer-item';
        me.footerClsCache = '.' + me.footerClsShortCache;
        me.labelClsShortCache = newBaseCls + '-item-label';
        me.labelClsCache = '.' + me.labelClsShortCache;
        me.disclosureClsShortCache = newBaseCls + '-disclosure';
        me.disclosureClsCache = '.' + me.disclosureClsShortCache;
        me.iconClsShortCache = newBaseCls + '-icon';
        me.iconClsCache = '.' + me.iconClsShortCache;
        this.callParent(arguments);
    },
    hiddenDisplayCache: Ext.baseCSSPrefix + 'hidden-display',
    getItemElementConfig: function(index, data) {
        var me = this,
            dataview = me.dataview,
            itemCls = dataview.getItemCls(),
            cls = me.itemClsShortCache,
            config, iconSrc;
        if (itemCls) {
            cls += ' ' + itemCls;
        }
        config = {
            cls: cls,
            children: [
                {
                    cls: me.labelClsShortCache,
                    html: me.renderItemTpl(index, data)
                }
            ]
        };
        if (dataview.getIcon()) {
            iconSrc = data.iconSrc;
            config.children.push({
                cls: me.iconClsShortCache,
                style: 'background-image: ' + iconSrc ? 'url("' + newSrc + '")' : ''
            });
        }
        if (dataview.getOnItemDisclosure()) {
            config.children.push({
                cls: me.disclosureClsShortCache + ' ' + ((data[dataview.getDisclosureProperty()] === false) ? me.hiddenDisplayCache : '')
            });
        }
        return config;
    },
    updateListItem: function(record, item) {
        var me = this,
            dataview = me.dataview,
            extItem = Ext.fly(item),
            innerItem = extItem.down(me.labelClsCache, true),
            store = databiew.getStore(),
            index = store.indexOf(record),
            data = dataview.prepareData(record.getData(true), index, record),
            disclosureProperty = dataview.getDisclosureProperty(),
            hasDisclosureProperty = data && data.hasOwnProperty(disclosureProperty),
            iconSrc = data && data.hasOwnProperty('iconSrc'),
            disclosureEl, iconEl;
        innerItem.innerHTML = me.renderItemTpl(index, data, store);
        if (hasDisclosureProperty) {
            disclosureEl = extItem.down(me.disclosureClsCache);
            disclosureEl[data[disclosureProperty] === false ? 'addCls' : 'removeCls'](me.hiddenDisplayCache);
        }
        if (dataview.getIcon()) {
            iconEl = extItem.down(me.iconClsCache, true);
            iconEl.style.backgroundImage = iconSrc ? 'url("' + iconSrc + '")' : '';
        }
    },
    doRemoveHeaders: function() {
        var me = this,
            headerClsShortCache = me.headerItemClsShortCache,
            existingHeaders = me.element.query(me.headerClsCache),
            existingHeadersLn = existingHeaders.length,
            i = 0,
            item;
        for (; i < existingHeadersLn; i++) {
            item = existingHeaders[i];
            Ext.fly(item.parentNode).removeCls(headerClsShortCache);
            Ext.get(item).destroy();
        }
    },
    doRemoveFooterCls: function() {
        var me = this,
            footerClsShortCache = me.footerClsShortCache,
            existingFooters = me.element.query(me.footerClsCache),
            existingFootersLn = existingFooters.length,
            i = 0;
        for (; i < existingFootersLn; i++) {
            Ext.fly(existingFooters[i]).removeCls(footerClsShortCache);
        }
    },
    doAddHeader: function(item, html) {
        item = Ext.fly(item);
        if (html) {
            item.insertFirst(Ext.Element.create({
                cls: this.headerClsShortCache,
                html: html
            }));
        }
        item.addCls(this.headerItemClsShortCache);
    },
    destroy: function() {
        this.doRemoveHeaders();
        this.callParent();
    }
});


Ext.define('Ext.field.Checkbox', {
    extend: Ext.field.Field,
    alternateClassName: 'Ext.form.Checkbox',
    xtype: 'checkboxfield',
    qsaLeftRe: /[\[]/g,
    qsaRightRe: /[\]]/g,
    isCheckbox: true,
    defaultBindProperty: 'checked',
    twoWayBindable: {
        checked: 1
    },
    publishes: {
        checked: 1
    },
    
    
    
    config: {
        
        ui: 'checkbox',
        
        value: '',
        
        checked: false,
        
        tabIndex: -1,
        
        component: {
            xtype: 'input',
            type: 'checkbox',
            useMask: true,
            cls: Ext.baseCSSPrefix + 'input-checkbox'
        }
    },
    
    
    initialize: function() {
        var me = this,
            component = me.getComponent();
        me.callParent();
        component.on({
            scope: me,
            order: 'before',
            masktap: 'onMaskTap'
        });
        component.doMaskTap = Ext.emptyFn;
        me.label.on({
            scope: me,
            tap: 'onMaskTap'
        });
        
        
        
        
        me.publishState('checked', me.getChecked());
    },
    
    doInitValue: function() {
        var me = this,
            initialConfig = me.getInitialConfig();
        
        if (initialConfig.hasOwnProperty('value')) {
            me.originalState = initialConfig.value;
        }
        if (initialConfig.hasOwnProperty('checked')) {
            me.originalState = initialConfig.checked;
        }
        me.callParent(arguments);
    },
    
    updateInputType: function(newInputType) {
        var component = this.getComponent();
        if (component) {
            component.setType(newInputType);
        }
    },
    
    updateName: function(newName) {
        var component = this.getComponent();
        if (component) {
            component.setName(newName);
        }
    },
    
    getSubmitValue: function() {
        return (this.getChecked()) ? Ext.isEmpty(this._value) ? true : this._value : null;
    },
    updateChecked: function(checked, oldChecked) {
        var me = this,
            eventName;
        me.getComponent().setChecked(checked);
        
        if (me.initialized) {
            eventName = checked ? 'check' : 'uncheck';
            me.fireEvent(eventName, me);
            me.fireEvent('change', me, checked, oldChecked);
        }
    },
    
    onMaskTap: function(component, e) {
        var me = this,
            dom = me.getComponent().input.dom;
        if (me.getDisabled()) {
            return false;
        }
        
        dom.checked = !dom.checked;
        me.setChecked(dom.checked);
        
        return false;
    },
    
    isChecked: function() {
        return this.getChecked();
    },
    
    check: function() {
        return this.setChecked(true);
    },
    
    uncheck: function() {
        return this.setChecked(false);
    },
    getSameGroupFields: function() {
        var me = this,
            component = me.up('formpanel') || me.up('fieldset'),
            name = me.getName(),
            replaceLeft = me.qsaLeftRe,
            replaceRight = me.qsaRightRe,
            
            baseCls = me.getBaseCls().split(' ').join('.'),
            components = [],
            elements, element, i, ln;
        if (!component) {
            
            Ext.Logger.warn('Ext.field.Radio components must always be descendants of an Ext.form.Panel or Ext.form.FieldSet.');
            
            component = Ext.Viewport;
        }
        
        name = name.replace(replaceLeft, '\\[');
        name = name.replace(replaceRight, '\\]');
        elements = Ext.query('[name=' + name + ']', component.element.dom);
        ln = elements.length;
        for (i = 0; i < ln; i++) {
            element = elements[i];
            element = Ext.fly(element).up('.' + baseCls);
            if (element && element.id) {
                components.push(Ext.getCmp(element.id));
            }
        }
        return components;
    },
    
    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.Picker', {
    extend: Ext.field.Text,
    config: {
        
        component: {
            useMask: true
        },
        
        clearIcon: false,
        
        usePicker: 'auto',
        
        defaultPhonePickerConfig: null,
        
        defaultTabletPickerConfig: null,
        
        pickerSlotAlign: 'center'
    },
    
    initialize: function() {
        var me = this,
            component = me.getComponent();
        me.callParent();
        component.on({
            scope: me,
            masktap: 'onMaskTap'
        });
        component.doMaskTap = Ext.emptyFn;
    },
    
    updateDefaultPhonePickerConfig: function(newConfig) {
        var phonePicker = this.phonePicker;
        if (phonePicker) {
            phonePicker.setConfig(newConfig);
        }
    },
    
    updateDefaultTabletPickerConfig: function(newConfig) {
        var tabletPicker = this.tabletPicker;
        if (tabletPicker) {
            tabletPicker.setConfig(newConfig);
        }
    },
    
    applyUsePicker: function(usePicker) {
        if (usePicker === 'auto') {
            usePicker = Ext.os.deviceType === 'Phone';
        }
        return Boolean(usePicker);
    },
    syncEmptyCls: Ext.emptyFn,
    
    onMaskTap: function() {
        if (!this.getDisabled()) {
            this.onFocus();
        }
        return false;
    },
    
    updateDisabled: function(disabled) {
        var component = this.getComponent();
        if (component) {
            component.setDisabled(disabled);
        }
        Ext.Component.prototype.updateDisabled.apply(this, arguments);
    },
    
    setDisabled: function() {
        Ext.Component.prototype.setDisabled.apply(this, arguments);
    },
    onFocus: function(e) {
        if (this.getDisabled()) {
            return false;
        }
        var component = this.getComponent();
        this.fireEvent('focus', this, e);
        if (Ext.os.is.Android4) {
            component.input.dom.focus();
        }
        component.input.dom.blur();
        this.isFocused = true;
        this.showPicker();
    },
    destroy: function() {
        var me = this;
        me.callParent();
        me.tabletPicker = me.phonePicker = Ext.destroy(me.tabletPicker, me.phonePicker);
    }
});


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,
            indicators: false,
            momentumEasing: {
                minVelocity: 2
            },
            slotSnapEasing: {
                duration: 100
            }
        },
        
        verticallyCenterItems: true
    },
    constructor: function() {
        
        this.selectedIndex = 0;
        
        this.callParent(arguments);
    },
    
    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() {
        this.callParent();
        var scroller = this.getScrollable();
        this.on({
            scope: this,
            painted: 'onPainted',
            itemtap: 'doItemTap'
        });
        this.element.on({
            scope: this,
            touchstart: 'onTouchStart',
            touchend: 'onTouchEnd'
        });
        scroller.on({
            scope: this,
            scrollend: 'onScrollEnd'
        });
    },
    
    onPainted: function() {
        this.setupBar();
    },
    
    getPicker: function() {
        if (!this.picker) {
            this.picker = this.getParent();
        }
        return this.picker;
    },
    
    setupBar: function() {
        if (!this.rendered) {
            
            return;
        }
        var element = this.element,
            containerElement = this.container.element,
            picker = this.getPicker(),
            bar = picker.bar,
            value = this.getValue(),
            showTitle = this.getShowTitle(),
            title = this.getTitle(),
            scroller = this.getScrollable(),
            titleHeight = 0,
            barHeight, padding;
        barHeight = bar.dom.getBoundingClientRect().height;
        if (showTitle && title) {
            titleHeight = title.element.getHeight();
        }
        padding = Math.ceil((element.getHeight() - titleHeight - barHeight) / 2);
        if (this.getVerticallyCenterItems()) {
            containerElement.setStyle({
                padding: padding + 'px 0 ' + padding + 'px'
            });
        }
        scroller.refresh();
        scroller.setSlotSnapSize(barHeight);
        this.setValue(value);
    },
    
    doItemTap: function(list, index, item, e) {
        var me = this;
        me.selectedIndex = index;
        me.selectedNode = item;
        me.scrollToItem(item, true);
    },
    
    scrollToItem: function(item, animated) {
        var y = item.getY(),
            parentEl = item.parent(),
            parentY = parentEl.getY(),
            scroller = this.getScrollable(),
            difference;
        difference = y - parentY;
        scroller.scrollTo(0, difference, animated);
    },
    
    onTouchStart: function() {
        this.element.addCls(Ext.baseCSSPrefix + 'scrolling');
    },
    
    onTouchEnd: function() {
        this.element.removeCls(Ext.baseCSSPrefix + 'scrolling');
    },
    
    onScrollEnd: function(scroller, x, y) {
        var me = this,
            index = Math.round(y / me.picker.bar.dom.getBoundingClientRect().height),
            viewItems = me.getViewItems(),
            item = viewItems[index];
        if (item) {
            me.selectedIndex = index;
            me.selectedNode = item;
            me.fireEvent('slotpick', me, me.getValue(), me.selectedNode);
        }
    },
    
    getValue: function(useDom) {
        var store = this.getStore(),
            record, value;
        if (!store) {
            return;
        }
        if (!this.rendered || !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) {
        if (!this.rendered) {
            
            this._value = value;
            return;
        }
        var store = this.getStore(),
            viewItems = this.getViewItems(),
            valueField = this.getValueField(),
            index, item;
        index = store.findExact(valueField, value);
        if (index == -1) {
            index = 0;
        }
        item = Ext.get(viewItems[index]);
        this.selectedIndex = index;
        if (item) {
            this.scrollToItem(item, (animated) ? {
                duration: 100
            } : false);
            this.select(this.selectedIndex);
        }
        this._value = value;
    }
});


Ext.define('Ext.picker.Picker', {
    extend: Ext.Sheet,
    alias: 'widget.picker',
    alternateClassName: 'Ext.Picker',
    isPicker: true,
    
    
    
    config: {
        
        baseCls: Ext.baseCSSPrefix + 'picker',
        
        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'
        }
    },
    initialize: function() {
        var me = this,
            clsPrefix = Ext.baseCSSPrefix,
            innerElement = this.innerElement;
        
        this.mask = innerElement.createChild({
            cls: clsPrefix + 'picker-mask'
        });
        this.bar = this.mask.createChild({
            cls: clsPrefix + 'picker-bar'
        });
        me.on({
            scope: this,
            delegate: 'pickerslot',
            slotpick: 'onSlotPick'
        });
    },
    
    applyToolbar: function(config) {
        if (config === true) {
            config = {};
        }
        Ext.applyIf(config, {
            docked: this.getToolbarPosition()
        });
        return Ext.factory(config, 'Ext.TitleBar', this.getToolbar());
    },
    
    updateToolbar: function(newToolbar, oldToolbar) {
        if (newToolbar) {
            this.add(newToolbar);
        }
        if (oldToolbar) {
            this.remove(oldToolbar);
        }
    },
    
    applyDoneButton: function(config) {
        if (config) {
            if (Ext.isBoolean(config)) {
                config = {};
            }
            if (typeof config == "string") {
                config = {
                    text: config
                };
            }
            Ext.applyIf(config, {
                ui: 'action',
                align: 'right',
                text: 'Done'
            });
        }
        return Ext.factory(config, 'Ext.Button', this.getDoneButton());
    },
    updateDoneButton: function(newDoneButton, oldDoneButton) {
        var toolbar = this.getToolbar();
        if (newDoneButton) {
            toolbar.add(newDoneButton);
            newDoneButton.on('tap', this.onDoneButtonTap, this);
        } else if (oldDoneButton) {
            toolbar.remove(oldDoneButton);
        }
    },
    
    applyCancelButton: function(config) {
        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', this.getCancelButton());
    },
    updateCancelButton: function(newCancelButton, oldCancelButton) {
        var toolbar = this.getToolbar();
        if (newCancelButton) {
            toolbar.add(newCancelButton);
            newCancelButton.on('tap', this.onCancelButtonTap, this);
        } else if (oldCancelButton) {
            toolbar.remove(oldCancelButton);
        }
    },
    
    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 bcss = Ext.baseCSSPrefix,
            innerItems;
        this.removeAll();
        if (newSlots) {
            this.add(newSlots);
        }
        innerItems = this.getInnerItems();
        if (innerItems.length > 0) {
            innerItems[0].addCls(bcss + 'first');
            innerItems[innerItems.length - 1].addCls(bcss + 'last');
        }
        this.updateUseTitles(this.getUseTitles());
    },
    
    onDoneButtonTap: function() {
        var oldValue = this._value,
            newValue = this.getValue(true);
        if (newValue != oldValue) {
            this.fireEvent('change', this, newValue);
        }
        this.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);
    },
    show: function() {
        if (this.getParent() === undefined) {
            Ext.Viewport.add(this);
        }
        this.callParent(arguments);
        if (!this.isHidden()) {
            this.setValue(this._value);
        }
        Ext.util.InputBlocker.blockInputs();
    },
    
    setValue: function(values, animated) {
        var me = this,
            slots = me.getInnerItems(),
            ln = slots.length,
            key, slot, loopSlot, i, value;
        if (!values) {
            values = {};
            for (i = 0; i < ln; i++) {
                
                values[slots[i].config.name] = null;
            }
        }
        for (key in values) {
            slot = null;
            value = values[key];
            for (i = 0; i < slots.length; i++) {
                loopSlot = slots[i];
                if (loopSlot.config.name == key) {
                    slot = loopSlot;
                    break;
                }
            }
            if (slot) {
                if (animated) {
                    slot.setValueAnimated(value);
                } else {
                    slot.setValue(value);
                }
            }
        }
        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();
    },
    destroy: function() {
        var me = this;
        me.callParent();
        me.mask = me.bar = Ext.destroy(me.mask, me.bar);
    }
});


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() {
        this.callParent();
        this.on({
            scope: this,
            delegate: '> slot',
            slotpick: this.onSlotPick
        });
        this.on({
            scope: this,
            show: this.onSlotPick
        });
    },
    setValue: function(value, animated) {
        if (Ext.isDate(value)) {
            value = {
                day: value.getDate(),
                month: value.getMonth() + 1,
                year: value.getFullYear()
            };
        }
        this.callParent([
            value,
            animated
        ]);
        this.onSlotPick();
    },
    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 instanceof Ext.picker.Slot) {
                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 = [],
            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
            });
        }
        var slots = [];
        slotOrder.forEach(function(item) {
            slots.push(me.createSlot(item, days, months, years));
        });
        me.setSlots(slots);
    },
    
    createSlot: function(name, days, months, years) {
        switch (name) {
            case 'year':
                return {
                    name: 'year',
                    align: 'center',
                    data: years,
                    title: this.getYearText(),
                    flex: 3
                };
            case 'month':
                return {
                    name: name,
                    align: 'right',
                    data: months,
                    title: this.getMonthText(),
                    flex: 4
                };
            case 'day':
                return {
                    name: 'day',
                    align: 'center',
                    data: days,
                    title: this.getDayText(),
                    flex: 2
                };
        }
    },
    onSlotPick: function() {
        var value = this.getValue(true),
            slot = this.getDaySlot(),
            year = value.getFullYear(),
            month = value.getMonth(),
            days = [],
            daysInMonth, i;
        if (!value || !Ext.isDate(value) || !slot) {
            return;
        }
        this.callParent(arguments);
        
        daysInMonth = this.getDaysInMonth(month + 1, year);
        for (i = 0; i < daysInMonth; i++) {
            days.push({
                text: i + 1,
                value: i + 1
            });
        }
        
        if (slot.getStore().getCount() == days.length) {
            return;
        }
        slot.getStore().setData(days);
        
        var store = slot.getStore(),
            viewItems = slot.getViewItems(),
            valueField = slot.getValueField(),
            index, item;
        index = store.find(valueField, value.getDate());
        if (index == -1) {
            return;
        }
        item = Ext.get(viewItems[index]);
        slot.selectedIndex = index;
        slot.scrollToItem(item);
        slot.setValue(slot.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 oldValue = this._value,
            newValue = this.getValue(true),
            testValue = newValue;
        if (Ext.isDate(newValue)) {
            testValue = newValue.toDateString();
        }
        if (Ext.isDate(oldValue)) {
            oldValue = oldValue.toDateString();
        }
        if (testValue != oldValue) {
            this.fireEvent('change', this, newValue);
        }
        this.hide();
        Ext.util.InputBlocker.unblockInputs();
    }
});


Ext.define('Ext.field.DatePicker', {
    extend: Ext.field.Picker,
    alternateClassName: 'Ext.form.DatePicker',
    xtype: 'datepickerfield',
    
    config: {
        ui: 'select',
        
        picker: true,
        
        
        destroyPickerOnHide: false,
        
        dateFormat: ''
    },
    applyValue: function(value, oldValue) {
        if (!Ext.isDate(value)) {
            if (value) {
                value = Ext.Date.parse(value, this.getDateFormat());
            } else {
                value = null;
            }
        }
        
        
        
        if (value && oldValue && value.getTime() === oldValue.getTime()) {
            value = undefined;
        }
        return value;
    },
    updateValue: function(value, oldValue) {
        var me = this,
            picker = me._picker;
        if (picker && picker.isPicker) {
            picker.setValue(value);
        }
        
        if (value !== null) {
            me.getComponent().setValue(Ext.Date.format(value, me.getDateFormat()));
        } else {
            me.getComponent().setValue('');
        }
        me.fireEvent('change', me, value, oldValue);
    },
    applyDateFormat: function(dateFormat) {
        return dateFormat || Ext.util.Format.defaultDateFormat;
    },
    
    updateDateFormat: function(newDateFormat) {
        var value = this.getValue();
        if (Ext.isDate(value)) {
            this.getComponent().setValue(Ext.Date.format(value, newDateFormat));
        }
    },
    
    
    getFormattedValue: function(format) {
        var value = this.getValue();
        return Ext.isDate(value) ? Ext.Date.format(value, format || this.getDateFormat()) : '';
    },
    applyPicker: function(picker, pickerInstance) {
        if (pickerInstance && pickerInstance.isPicker) {
            picker = pickerInstance.setConfig(picker);
        }
        return picker;
    },
    getPicker: function() {
        var picker = this._picker,
            value = this.getValue();
        if (picker && !picker.isPicker) {
            picker = Ext.factory(picker, Ext.picker.Date);
            if (value !== null) {
                picker.setValue(value);
            }
        }
        picker.on({
            scope: this,
            change: 'onPickerChange',
            hide: 'onPickerHide'
        });
        this._picker = picker;
        return picker;
    },
    
    onPickerChange: function(picker, value) {
        var me = this,
            oldValue = me.getValue();
        me.setValue(value);
        me.fireEvent('select', me, value);
    },
    
    onPickerHide: function() {
        var me = this,
            picker = me.getPicker();
        if (me.getDestroyPickerOnHide() && picker) {
            picker.destroy();
            me._picker = me.getInitialConfig().picker || true;
        }
    },
    reset: function() {
        this.setValue(this.originalValue);
    },
    onFocus: function(e) {
        var component = this.getComponent();
        this.fireEvent('focus', this, e);
        if (Ext.os.is.Android4) {
            component.input.dom.focus();
        }
        component.input.dom.blur();
        if (this.getReadOnly()) {
            return false;
        }
        this.isFocused = true;
        this.getPicker().show();
    },
    
    destroy: function() {
        var picker = this._picker;
        if (picker && picker.isPicker) {
            picker.destroy();
        }
        this.callParent();
    }
});


Ext.define('Ext.field.DatePickerNative', {
    extend: Ext.field.DatePicker,
    alternateClassName: 'Ext.form.DatePickerNative',
    xtype: 'datepickernativefield',
    initialize: function() {
        this.callParent();
    },
    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.Email', {
    extend: Ext.field.Text,
    alternateClassName: 'Ext.form.Email',
    xtype: 'emailfield',
    config: {
        
        component: {
            type: 'email'
        },
        
        autoCapitalize: false
    }
});


Ext.define('Ext.field.FileInput', {
    extend: Ext.field.Input,
    xtype: 'fileinput',
    
    config: {
        type: "file",
        accept: null,
        capture: null,
        name: null,
        multiple: false
    },
    
    captureLookup: {
        video: "camcorder",
        image: "camera",
        audio: "microphone"
    },
    
    initialize: function() {
        var me = this;
        me.callParent();
        me.input.on({
            scope: me,
            change: 'onInputChange'
        });
    },
    
    getValue: function() {
        var input = this.input;
        if (input) {
            this._value = input.dom.value;
        }
        return this._value;
    },
    
    setValue: function(newValue) {
        var oldValue = this._value;
        this._value = newValue;
        if (String(this._value) != String(oldValue) && this.initialized) {
            this.onChange(this, this._value, oldValue);
        }
        return this;
    },
    
    getFiles: function() {
        var input = this.input;
        if (input) {
            this.$files = input.dom.files;
        }
        return this.$files;
    },
    
    onInputChange: function(e) {
        this.setValue(e.target.value);
    },
    
    onChange: function(me, value, startValue) {
        this.fireEvent('change', me, value, startValue);
    },
    
    applyName: function(value) {
        if (this.getMultiple() && value.substr(-2, 2) !== "[]") {
            value += "[]";
        } else if ((!this.getMultiple()) && value.substr(-2, 2) === "[]") {
            value = value.substr(0, value.length - 2);
        }
        return value;
    },
    
    applyMultiple: function(value) {
        this.updateFieldAttribute('multiple', value ? '' : null);
        return value;
    },
    
    updateMultiple: function() {
        var name = this.getName();
        if (!Ext.isEmpty(name)) {
            this.setName(name);
        }
    },
    
    applyAccept: function(value) {
        switch (value) {
            case "video":
            case "audio":
            case "image":
                value = value + "/*";
                break;
        }
        this.updateFieldAttribute('accept', value);
    },
    
    applyCapture: function(value) {
        this.updateFieldAttribute('capture', value);
        return value;
    }
});


Ext.define('Ext.field.File', {
    extend: Ext.field.Field,
    xtype: 'filefield',
    
    config: {
        component: {
            xtype: 'fileinput',
            fastFocus: false
        }
    },
    proxyConfig: {
        name: null,
        value: null,
        files: null,
        
        multiple: false,
        
        accept: null,
        
        capture: null
    },
    
    isFile: true,
    
    initialize: function() {
        var me = this;
        me.callParent();
        me.getComponent().on({
            scope: this,
            change: 'onChange'
        });
    },
    onChange: function(me, value, startValue) {
        me.fireEvent('change', this, value, startValue);
    }
});


Ext.define('Ext.field.Hidden', {
    extend: Ext.field.Text,
    alternateClassName: 'Ext.form.Hidden',
    xtype: 'hiddenfield',
    config: {
        
        component: {
            xtype: 'input',
            type: 'hidden'
        },
        
        ui: 'hidden',
        
        hidden: true,
        
        tabIndex: -1
    }
});


Ext.define('Ext.field.Number', {
    extend: Ext.field.Text,
    xtype: 'numberfield',
    alternateClassName: 'Ext.form.Number',
    
    config: {
        
        component: {
            type: 'number'
        },
        
        ui: 'number'
    },
    proxyConfig: {
        
        minValue: null,
        
        maxValue: null,
        
        stepValue: null
    },
    applyPlaceHolder: function(value) {
        
        
        this._enableNumericPlaceHolderHack = ((!Ext.feature.has.NumericInputPlaceHolder) && (!Ext.isEmpty(value)));
        return value;
    },
    onFocus: function(e) {
        if (this._enableNumericPlaceHolderHack) {
            this.getComponent().input.dom.setAttribute("type", "number");
        }
        this.callParent(arguments);
    },
    onBlur: function(e) {
        if (this._enableNumericPlaceHolderHack) {
            this.getComponent().input.dom.setAttribute("type", "text");
        }
        this.callParent(arguments);
    },
    doInitValue: function() {
        var value = this.getInitialConfig().value;
        if (value) {
            value = this.applyValue(value);
        }
        this.originalValue = value;
    },
    applyValue: function(value) {
        var minValue = this.getMinValue(),
            maxValue = this.getMaxValue();
        if (Ext.isNumber(minValue) && Ext.isNumber(value)) {
            value = Math.max(value, minValue);
        }
        if (Ext.isNumber(maxValue) && Ext.isNumber(value)) {
            value = Math.min(value, maxValue);
        }
        value = parseFloat(value);
        return (isNaN(value)) ? '' : value;
    },
    getValue: function() {
        var value = parseFloat(this.callParent(), 10);
        return (isNaN(value)) ? null : value;
    },
    doClearIconTap: function(me, e) {
        me.getComponent().setValue('');
        me.getValue();
        me.hideClearIcon();
    }
});


Ext.define('Ext.field.Password', {
    extend: Ext.field.Text,
    xtype: 'passwordfield',
    alternateClassName: 'Ext.form.Password',
    config: {
        
        autoCapitalize: false,
        
        revealable: false,
        
        revealed: false,
        
        component: {
            type: 'password'
        }
    },
    isPassword: true,
    initialize: function() {
        this.callParent(arguments);
        this.addCls(Ext.baseCSSPrefix + 'field-password');
    },
    updateRevealable: function(newValue, oldValue) {
        if (this.$revealIcon) {
            this.getComponent().element.removeChild(this.$revealIcon);
            this.$revealIcon = null;
        }
        if (newValue === true) {
            this.$revealIcon = new Ext.Element(Ext.Element.create({
                cls: 'x-reveal-icon'
            }, true));
            this.$revealIcon.on({
                tap: 'onRevealIconTap',
                touchstart: 'onRevealIconPress',
                touchend: 'onRevealIconRelease',
                scope: this
            });
            this.getComponent().element.appendChild(this.$revealIcon);
        }
    },
    updateRevealed: function(newValue, oldValue) {
        var component = this.getComponent();
        if (newValue) {
            this.element.addCls(Ext.baseCSSPrefix + 'revealed');
            component.setType("text");
        } else {
            this.element.removeCls(Ext.baseCSSPrefix + 'revealed');
            component.setType("password");
        }
    },
    
    updateValue: function(value, oldValue) {
        this.toggleRevealIcon(this.isValidTextValue(value));
        this.callParent([
            value,
            oldValue
        ]);
    },
    doKeyUp: function(me, e) {
        var value = me.getValue(),
            valid = me.isValidTextValue(me.getValue());
        me.toggleClearIcon(valid);
        if (e.browserEvent.keyCode === 13) {
            me.fireAction('action', [
                me,
                e
            ], 'doAction');
        }
        me.toggleRevealIcon(valid);
    },
    
    showRevealIcon: function() {
        var me = this,
            value = me.getValue(),
            
            valueValid = value !== undefined && value !== null && value !== "";
        if (me.getRevealable() && !me.getDisabled() && valueValid) {
            me.element.addCls(Ext.baseCSSPrefix + 'field-revealable');
        }
        return me;
    },
    
    hideRevealIcon: function() {
        if (this.getRevealable()) {
            this.element.removeCls(Ext.baseCSSPrefix + 'field-revealable');
        }
    },
    onRevealIconTap: function(e) {
        this.fireAction('revealicontap', [
            this,
            e
        ], 'doRevealIconTap');
    },
    
    doRevealIconTap: function(me, e) {
        me.setRevealed(!this.getRevealed());
    },
    onRevealIconPress: function() {
        this.$revealIcon.addCls(Ext.baseCSSPrefix + 'pressing');
    },
    onRevealIconRelease: function() {
        this.$revealIcon.removeCls(Ext.baseCSSPrefix + 'pressing');
    },
    privates: {
        isValidTextValue: function(value) {
            
            return (value !== undefined && value !== null && value !== '');
        },
        toggleRevealIcon: function(state) {
            if (state) {
                this.showRevealIcon();
            } else {
                this.hideRevealIcon();
            }
        }
    }
});


Ext.define('Ext.field.Radio', {
    extend: Ext.field.Checkbox,
    xtype: 'radiofield',
    alternateClassName: 'Ext.form.Radio',
    isRadio: true,
    config: {
        
        ui: 'radio',
        
        component: {
            type: 'radio',
            cls: Ext.baseCSSPrefix + 'input-radio'
        }
    },
    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);
        }
    },
    
    onMaskTap: function(component, e) {
        var me = this,
            dom = me.getComponent().input.dom;
        if (me.getDisabled()) {
            return false;
        }
        me.setChecked(true);
        
        return false;
    },
    
    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.Search', {
    extend: Ext.field.Text,
    xtype: 'searchfield',
    alternateClassName: 'Ext.form.Search',
    config: {
        
        component: {
            type: 'search'
        },
        
        ui: 'search'
    }
});


Ext.define('Ext.field.Select', {
    extend: Ext.field.Picker,
    xtype: 'selectfield',
    alternateClassName: 'Ext.form.Select',
    
    
    config: {
        
        ui: 'select',
        
        
        valueField: 'value',
        
        displayField: 'text',
        
        store: null,
        
        options: null,
        
        hiddenName: null,
        
        autoSelect: true,
        
        name: 'picker',
        
        selection: null
    },
    twoWayBindable: {
        selection: 1
    },
    publishes: {
        selection: 1
    },
    
    applyValue: function(value) {
        var me = this,
            record = value,
            index, store;
        
        
        me.getOptions();
        store = me.getStore();
        if ((value || value === 0) && !value.isModel && store) {
            index = store.find(me.getValueField(), value, null, null, null, true);
            if (index === -1) {
                index = store.find(me.getDisplayField(), value, null, null, null, true);
            }
            record = store.getAt(index);
        }
        return record;
    },
    updateValue: function(value, oldValue) {
        var me = this,
            component = me.getComponent(),
            displayValue = '';
        if (value === null || (value && value.isModel)) {
            me.settingSelection = true;
            me.setSelection(value);
            me.settingSelection = false;
            if (value) {
                displayValue = value.get(me.getDisplayField());
            }
        }
        if (component) {
            component.setValue(displayValue);
        }
        if (me.initialized) {
            me.fireEvent('change', me, value, oldValue);
        }
    },
    
    getValue: function() {
        var selection = this.getSelection();
        return selection ? selection.get(this.getValueField()) : null;
    },
    applySelection: function(selection) {
        return selection || null;
    },
    updateSelection: function(selection) {
        if (!this.settingSelection) {
            this.setValue(selection ? selection.get(this.getValueField()) : null);
        }
    },
    
    getPhonePicker: function() {
        var me = this,
            phonePicker = me.phonePicker,
            config;
        if (!phonePicker) {
            config = me.getDefaultPhonePickerConfig();
            me.phonePicker = phonePicker = Ext.create('Ext.picker.Picker', Ext.apply({
                slots: [
                    {
                        align: me.getPickerSlotAlign(),
                        name: me.getName(),
                        valueField: me.getValueField(),
                        displayField: me.getDisplayField(),
                        value: me.getValue(),
                        store: me.getStore()
                    }
                ],
                listeners: {
                    change: me.onPickerChange,
                    scope: me
                }
            }, config));
        }
        return phonePicker;
    },
    
    getTabletPicker: function() {
        var me = this,
            tabletPicker = me.tabletPicker,
            config;
        if (!tabletPicker) {
            config = me.getDefaultTabletPickerConfig();
            me.tabletPicker = tabletPicker = Ext.create('Ext.Panel', Ext.apply({
                left: 0,
                top: 0,
                modal: true,
                cls: Ext.baseCSSPrefix + 'select-overlay',
                layout: 'fit',
                hideOnMaskTap: true,
                width: Ext.os.is.Phone ? '14em' : '18em',
                height: (Ext.os.is.BlackBerry && Ext.os.version.getMajor() === 10) ? '12em' : (Ext.os.is.Phone ? '12.5em' : '22em'),
                items: {
                    xtype: 'list',
                    store: me.getStore(),
                    itemTpl: '<span class="x-list-label">{' + me.getDisplayField() + ':htmlEncode}</span>',
                    listeners: {
                        select: me.onListSelect,
                        itemtap: me.onListTap,
                        scope: me
                    }
                }
            }, config));
        }
        return tabletPicker;
    },
    
    showPicker: function() {
        var me = this,
            store = me.getStore(),
            value = me.getValue(),
            picker, name, pickerValue, list, index, record;
        
        if (!store || store.getCount() === 0) {
            return;
        }
        if (me.getReadOnly()) {
            return;
        }
        me.isFocused = true;
        if (me.getUsePicker()) {
            picker = me.getPhonePicker();
            name = me.getName();
            pickerValue = {};
            pickerValue[name] = value;
            picker.setValue(pickerValue);
            if (!picker.getParent()) {
                Ext.Viewport.add(picker);
            }
            picker.show();
        } else {
            picker = me.getTabletPicker();
            list = picker.down('list');
            if (!picker.getParent()) {
                Ext.Viewport.add(picker);
            }
            picker.showBy(me.getComponent(), null);
            if (value || me.getAutoSelect()) {
                store = list.getStore();
                index = store.find(me.getValueField(), value, null, null, null, true);
                record = store.getAt(index);
                if (record) {
                    list.select(record, null, true);
                }
            }
        }
    },
    
    onListSelect: function(item, record) {
        var me = this;
        if (record) {
            me.setValue(record);
        }
    },
    onListTap: function() {
        this.tabletPicker.hide({
            type: 'fade',
            out: true,
            scope: this
        });
    },
    
    onPickerChange: function(picker, value) {
        var me = this,
            newValue = value[me.getName()],
            store = me.getStore(),
            index = store.find(me.getValueField(), newValue, null, null, null, true),
            record = store.getAt(index);
        me.setValue(record);
    },
    
    updateOptions: function(newOptions) {
        var store = this.getStore();
        if (!store) {
            this.setStore(true);
            store = this._store;
        }
        if (!newOptions) {
            store.clearData();
        } else {
            store.setData(newOptions);
            this.onStoreDataChanged(store);
        }
        return this;
    },
    applyStore: function(store) {
        if (store === true) {
            store = Ext.create('Ext.data.Store', {
                fields: [
                    this.getValueField(),
                    this.getDisplayField()
                ],
                autoDestroy: true
            });
        }
        if (store) {
            store = Ext.data.StoreManager.lookup(store);
        }
        return store;
    },
    updateStore: function(store, oldStore) {
        var me = this,
            tabletPicker = me.tabletPicker,
            phonePicker = me.phonePicker;
        if (oldStore && oldStore.getAutoDestroy()) {
            oldStore.destroy();
        }
        if (store) {
            store.on({
                scope: this,
                add: 'onStoreDataChanged',
                remove: 'onStoreDataChanged',
                update: 'onStoreDataChanged',
                refresh: 'onStoreDataChanged'
            });
            me.onStoreDataChanged(store);
        }
        if (me.getUsePicker() && phonePicker) {
            phonePicker.down('pickerslot').setStore(store);
        } else if (tabletPicker) {
            tabletPicker.down('dataview').setStore(store);
        }
    },
    
    onStoreDataChanged: function(store) {
        var initialConfig = this.getInitialConfig(),
            value = this.getValue();
        if (value || value === 0) {
            this.setValue(value);
        }
        if (this.getValue() === null) {
            if (initialConfig.hasOwnProperty('value')) {
                this.setValue(initialConfig.value);
            }
            if (this.getValue() === null && this.getAutoSelect()) {
                if (store.getCount() > 0) {
                    this.setValue(store.getAt(0));
                }
            }
        }
    },
    
    reset: function() {
        var me = this,
            record, store, usePicker, picker;
        if (me.getAutoSelect()) {
            store = me.getStore();
            record = me.originalValue ? me.originalValue : store.getAt(0);
        } else {
            usePicker = me.getUsePicker();
            picker = usePicker ? me.phonePicker : me.tabletPicker;
            if (picker) {
                picker = picker.child(usePicker ? 'pickerslot' : 'dataview');
                picker.deselectAll();
            }
            record = null;
        }
        me.setValue(record);
        return me;
    },
    destroy: function() {
        var store = this.getStore();
        if (store && store.getAutoDestroy()) {
            store.destroy();
        }
        this.callParent();
    }
});


Ext.define('Ext.slider.Thumb', {
    extend: Ext.Component,
    xtype: 'thumb',
    config: {
        
        baseCls: Ext.baseCSSPrefix + 'thumb',
        
        pressedCls: Ext.baseCSSPrefix + 'thumb-pressing',
        
        draggable: {
            direction: 'horizontal'
        }
    },
    
    
    platformConfig: {
        ie10: {
            draggable: {
                translatable: {
                    translationMethod: 'csstransform'
                }
            }
        }
    },
    elementWidth: 0,
    initialize: function() {
        var me = this;
        me.callParent();
        me.getDraggable().onBefore({
            beforedragstart: 'onBeforeDragStart',
            dragstart: 'onDragStart',
            drag: 'onDrag',
            dragend: 'onDragEnd',
            scope: this
        });
        me.getDraggable().on({
            touchstart: 'onPress',
            touchend: 'onRelease',
            scope: me
        });
        me.element.on('resize', 'onElementResize', me);
    },
    
    updatePressedCls: function(pressedCls, oldPressedCls) {
        var element = this.element;
        if (element.hasCls(oldPressedCls)) {
            element.replaceCls(oldPressedCls, pressedCls);
        }
    },
    
    onPress: function() {
        var me = this,
            element = me.element,
            pressedCls = me.getPressedCls();
        if (!me.getDisabled()) {
            element.addCls(pressedCls);
        }
    },
    
    onRelease: function(e) {
        this.fireAction('release', [
            this,
            e
        ], 'doRelease');
    },
    
    doRelease: function(me, e) {
        if (!me.getDisabled()) {
            me.element.removeCls(me.getPressedCls());
        }
    },
    onBeforeDragStart: function(draggable, e, x, y) {
        if (this.isDisabled()) {
            return false;
        }
        return this.fireEvent('beforedragstart', this, e, x, y);
    },
    onDragStart: function(draggable, e, x, y) {
        this.fireEvent('dragstart', this, e, x, y);
    },
    onDrag: function(draggable, e, x, y) {
        if (this.isDisabled()) {
            return false;
        }
        this.fireEvent('drag', this, e, x, y);
    },
    onDragEnd: function(draggable, e, x, y) {
        if (this.isDisabled()) {
            return false;
        }
        this.fireEvent('dragend', this, e, x, y);
    },
    onElementResize: function(element, info) {
        this.elementWidth = info.width;
    },
    getElementWidth: function() {
        return this.elementWidth;
    }
});


Ext.define('Ext.slider.Slider', {
    extend: Ext.Container,
    xtype: 'slider',
    
    
    
    
    config: {
        baseCls: 'x-slider',
        
        thumbConfig: {
            draggable: {
                translatable: {
                    easingX: {
                        duration: 300,
                        type: 'ease-out'
                    }
                }
            }
        },
        
        increment: 1,
        
        value: 0,
        
        minValue: 0,
        
        maxValue: 100,
        
        allowThumbsOverlapping: false,
        
        animation: true,
        
        readOnly: false
    },
    
    elementWidth: 0,
    offsetValueRatio: 0,
    activeThumb: null,
    constructor: function(config) {
        config = config || {};
        if (config.hasOwnProperty('values')) {
            config.value = config.values;
        }
        this.callParent([
            config
        ]);
    },
    
    initialize: function() {
        var element = this.element,
            thumb;
        this.callParent();
        element.on({
            scope: this,
            tap: 'onTap',
            resize: 'onResize'
        });
        this.on({
            scope: this,
            delegate: '> thumb',
            tap: 'onTap',
            beforedragstart: 'onThumbBeforeDragStart',
            dragstart: 'onThumbDragStart',
            drag: 'onThumbDrag',
            dragend: 'onThumbDragEnd'
        });
        thumb = this.getThumb(0);
        if (thumb) {
            thumb.on('resize', 'onThumbResize', this);
        }
    },
    
    factoryThumb: function() {
        return Ext.factory(this.getThumbConfig(), Ext.slider.Thumb);
    },
    
    getThumbs: function() {
        return this.innerItems;
    },
    
    getThumb: function(index) {
        if (typeof index != 'number') {
            index = 0;
        }
        return this.innerItems[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() {
        var thumb = this.getThumb(0);
        if (thumb) {
            this.thumbWidth = thumb.getElementWidth();
        }
        this.refresh();
    },
    onResize: function(element, info) {
        this.elementWidth = info.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;
        me.refreshAllThumbConstraints();
        e.stopPropagation();
        if (me.getAllowThumbsOverlapping()) {
            me.setActiveThumb(thumb);
        }
        me.dragStartValue = me.getValue()[me.getThumbIndex(thumb)];
        me.fireEvent('dragstart', me, thumb, me.dragStartValue, e);
    },
    onThumbDrag: 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.getValue(), e);
        return false;
    },
    setIndexValue: function(index, value, animation) {
        var me = this,
            thumb = me.getThumb(index),
            values = me.getValue(),
            minValue = me.getMinValue(),
            offsetValueRatio = me.offsetValueRatio,
            increment = me.getIncrement(),
            draggable = thumb.getDraggable();
        draggable.setOffset((value - minValue) * offsetValueRatio, null, animation);
        values[index] = minValue + Math.round((draggable.offset.x / offsetValueRatio) / increment) * increment;
    },
    onThumbDragEnd: function(thumb, e) {
        var me = this,
            index = me.getThumbIndex(thumb),
            newValue = me.getValue()[index],
            oldValue = me.dragStartValue;
        me.snapThumbPosition(thumb, newValue);
        me.fireEvent('dragend', me, thumb, me.getValue(), e);
        if (oldValue !== newValue) {
            me.fireEvent('change', me, thumb, newValue, oldValue);
        }
    },
    getThumbIndex: function(thumb) {
        return this.getThumbs().indexOf(thumb);
    },
    refreshThumbConstraints: function(thumb) {
        var allowThumbsOverlapping = this.getAllowThumbsOverlapping(),
            offsetX = thumb.getDraggable().getOffset().x,
            thumbs = this.getThumbs(),
            index = this.getThumbIndex(thumb),
            previousThumb = thumbs[index - 1],
            nextThumb = thumbs[index + 1],
            thumbWidth = this.thumbWidth;
        if (previousThumb) {
            previousThumb.getDraggable().addExtraConstraint({
                max: {
                    x: offsetX - ((allowThumbsOverlapping) ? 0 : thumbWidth)
                }
            });
        }
        if (nextThumb) {
            nextThumb.getDraggable().addExtraConstraint({
                min: {
                    x: offsetX + ((allowThumbsOverlapping) ? 0 : 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.getValue();
        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.getThumb(closestIndex);
        me.setIndexValue(closestIndex, value, me.getAnimation());
        me.refreshThumbConstraints(thumb);
        if (oldValue !== value) {
            me.fireEvent('change', me, thumb, value, oldValue);
        }
    },
    
    updateThumbs: function(newThumbs) {
        this.add(newThumbs);
    },
    applyValue: function(value, oldValue) {
        var values = Ext.Array.from(value || 0),
            filteredValues = [],
            previousFilteredValue = this.getMinValue(),
            filteredValue, i, ln;
        for (i = 0 , ln = values.length; i < ln; i++) {
            filteredValue = this.constrainValue(values[i]);
            if (filteredValue < previousFilteredValue) {
                
                Ext.Logger.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 (!this.refreshing && oldValue) {
            if (Ext.Array.equals(value, oldValue)) {
                filteredValues = undefined;
            }
        }
        return filteredValues;
    },
    
    updateValue: function(newValue, oldValue) {
        var me = this,
            thumbs = me.getThumbs(),
            len = newValue.length,
            i;
        me.setThumbsCount(len);
        for (i = 0; i < len; i++) {
            me.snapThumbPosition(thumbs[i], newValue[i]);
        }
    },
    
    refreshValue: function() {
        this.refreshOffsetValueRatio();
        this.setValue(this.getValue());
    },
    
    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 thumbs = this.getThumbs(),
            thumbsCount = thumbs.length,
            i, ln, thumb;
        if (thumbsCount > count) {
            for (i = 0 , ln = thumbsCount - count; i < ln; i++) {
                thumb = thumbs[thumbs.length - 1];
                thumb.destroy();
            }
        } else if (thumbsCount < count) {
            for (i = 0 , ln = count - thumbsCount; i < ln; i++) {
                this.add(this.factoryThumb());
            }
        }
        return this;
    },
    
    setValues: function(value) {
        this.setValue(value);
    },
    
    getValues: function() {
        return this.getValue();
    },
    
    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 items = this.getItems().items,
            ln = items.length,
            i;
        for (i = 0; i < ln; i++) {
            items[i].setDisabled(disabled);
        }
    },
    privates: {
        refreshAllThumbConstraints: function() {
            var thumbs = this.getThumbs(),
                len = thumbs.length,
                i;
            for (i = 0; i < len; i++) {
                this.refreshThumbConstraints(thumbs[i]);
            }
        },
        snapThumbPosition: function(thumb, value) {
            var ratio = this.offsetValueRatio,
                offset;
            if (isFinite(ratio)) {
                offset = Ext.Number.correctFloat((value - this.getMinValue()) * ratio);
                thumb.getDraggable().setExtraConstraint(null).setOffset(offset);
            }
        }
    }
});


Ext.define('Ext.field.Slider', {
    extend: Ext.field.Field,
    xtype: 'sliderfield',
    alternateClassName: 'Ext.form.Slider',
    
    
    
    
    
    config: {
        
        cls: Ext.baseCSSPrefix + 'slider-field',
        
        liveUpdate: false,
        
        tabIndex: -1,
        
        readOnly: false,
        
        value: 0
    },
    proxyConfig: {
        
        increment: 1,
        
        minValue: 0,
        
        maxValue: 100
    },
    defaultBindProperty: 'values',
    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.getComponent().on({
            scope: this,
            change: 'onSliderChange',
            dragstart: 'onSliderDragStart',
            drag: 'onSliderDrag',
            dragend: 'onSliderDragEnd'
        });
    },
    
    applyComponent: function(config) {
        return Ext.factory(config, Ext.slider.Slider);
    },
    
    updateComponent: function(component, oldComponent) {
        this.callParent([
            component,
            oldComponent
        ]);
        component.setMinValue(this.getMinValue());
        component.setMaxValue(this.getMaxValue());
    },
    applyValue: function(value) {
        var ret = value;
        
        
        if (this.dragging && this.isSyncing('value')) {
            ret = undefined;
        }
        return ret;
    },
    updateValue: function(value, oldValue) {
        var me = this;
        if (!me.dragging) {
            me.setComponentValue(value);
        }
        if (me.initialized) {
            me.fireEvent('change', me, value, oldValue);
        }
    },
    setComponentValue: function(value) {
        this.getComponent().setValue(value);
    },
    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);
    },
    updateDisabled: function(disabled) {
        this.callParent(arguments);
        this.getComponent().setDisabled(disabled);
    },
    updateReadOnly: function(newValue) {
        this.getComponent().setReadOnly(newValue);
    },
    isDirty: function() {
        if (this.getDisabled()) {
            return false;
        }
        return this.getValue() !== this.originalValue;
    },
    updateMultipleState: function() {
        var value = this.getValue();
        if (value && value.length > 1) {
            this.addCls(Ext.baseCSSPrefix + 'slider-multiple');
        }
    }
});


Ext.define('Ext.field.SingleSlider', {
    extend: Ext.field.Slider,
    xtype: 'singlesliderfield',
    
    
    
    
    
    
    defaultBindProperty: 'value',
    publishes: {
        value: 1
    },
    initialize: function() {
        this.callParent();
        this.publishState('value', this.getValue());
    },
    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.util.TapRepeater', {
    mixins: {
        observable: Ext.mixin.Observable
    },
    
    
    
    config: {
        el: null,
        accelerate: true,
        interval: 10,
        delay: 250,
        preventDefault: true,
        stopDefault: false,
        timer: 0,
        pressCls: null
    },
    
    constructor: function(config) {
        var me = this;
        
        for (var configName in config) {
            if (me.self.prototype.config && !(configName in me.self.prototype.config)) {
                me[configName] = config[configName];
                Ext.Logger.warn('Applied config as instance property: "' + configName + '"', me);
            }
        }
        
        me.mixins.observable.constructor.call(me, config);
    },
    updateEl: function(newEl, oldEl) {
        var eventCfg = {
                touchstart: 'onTouchStart',
                touchend: 'onTouchEnd',
                tap: 'eventOptions',
                scope: this
            };
        if (oldEl) {
            oldEl.un(eventCfg);
        }
        newEl.on(eventCfg);
    },
    
    eventOptions: function(e) {
        if (this.getPreventDefault()) {
            e.preventDefault();
        }
        if (this.getStopDefault()) {
            e.stopEvent();
        }
    },
    destroy: function() {
        this.el = Ext.destroy(this.el);
        this.callParent();
    },
    
    onTouchStart: function(e) {
        var me = this,
            pressCls = me.getPressCls();
        clearTimeout(me.getTimer());
        if (pressCls) {
            me.getEl().addCls(pressCls);
        }
        me.tapStartTime = new Date();
        me.fireEvent('touchstart', me, e);
        me.fireEvent('tap', me, e);
        
        if (me.getAccelerate()) {
            me.delay = 400;
        }
        me.setTimer(Ext.defer(me.tap, me.getDelay() || me.getInterval(), me, [
            e
        ]));
    },
    
    tap: function(e) {
        var me = this;
        me.fireEvent('tap', me, e);
        me.setTimer(Ext.defer(me.tap, me.getAccelerate() ? me.easeOutExpo(Ext.Date.getElapsed(me.tapStartTime), 400, -390, 12000) : me.getInterval(), me, [
            e
        ]));
    },
    
    easeOutExpo: function(t, b, c, d) {
        return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
    },
    
    onTouchEnd: function(e) {
        var me = this;
        clearTimeout(me.getTimer());
        me.getEl().removeCls(me.getPressCls());
        me.fireEvent('touchend', me, e);
    }
});


Ext.define('Ext.field.Spinner', {
    extend: Ext.field.Number,
    xtype: 'spinnerfield',
    alternateClassName: 'Ext.form.Spinner',
    
    
    
    
    
    config: {
        
        cls: Ext.baseCSSPrefix + 'spinner',
        
        minValue: Number.NEGATIVE_INFINITY,
        
        maxValue: Number.MAX_VALUE,
        
        stepValue: 0.1,
        
        accelerateOnTapHold: true,
        
        cycle: false,
        
        clearIcon: false,
        
        defaultValue: 0,
        
        tabIndex: -1,
        
        groupButtons: true,
        
        component: {
            disabled: true
        },
        
        value: undefined
    },
    platformConfig: {
        android: {
            component: {
                disabled: false,
                readOnly: true
            }
        }
    },
    syncEmptyCls: Ext.emptyFn,
    
    updateComponent: function(newComponent) {
        var me = this,
            cls = me.getCls();
        me.callParent(arguments);
        if (newComponent) {
            me.spinDownButton = Ext.Element.create({
                cls: cls + '-button ' + cls + '-button-down'
            });
            me.spinUpButton = Ext.Element.create({
                cls: cls + '-button ' + cls + '-button-up'
            });
            me.downRepeater = me.createRepeater(me.spinDownButton, me.onSpinDown);
            me.upRepeater = me.createRepeater(me.spinUpButton, me.onSpinUp);
        }
    },
    updateGroupButtons: function(newGroupButtons, oldGroupButtons) {
        var me = this,
            innerElement = me.innerElement,
            cls = me.getBaseCls() + '-grouped-buttons';
        me.getComponent();
        if (newGroupButtons != oldGroupButtons) {
            if (newGroupButtons) {
                me.addCls(cls);
                innerElement.appendChild(me.spinDownButton);
                innerElement.appendChild(me.spinUpButton);
            } else {
                me.removeCls(cls);
                innerElement.insertFirst(me.spinDownButton);
                innerElement.appendChild(me.spinUpButton);
            }
        }
    },
    applyValue: function(value) {
        value = parseFloat(value);
        if (isNaN(value) || value === null) {
            value = this.getDefaultValue();
        }
        
        value = Math.round(value * 10) / 10;
        return this.callParent([
            value
        ]);
    },
    
    createRepeater: function(el, fn) {
        var me = this,
            repeater = Ext.create('Ext.util.TapRepeater', {
                el: el,
                accelerate: me.getAccelerateOnTapHold()
            });
        repeater.on({
            tap: fn,
            touchstart: 'onTouchStart',
            touchend: 'onTouchEnd',
            scope: me
        });
        return repeater;
    },
    
    onSpinDown: function() {
        if (!this.getDisabled() && !this.getReadOnly()) {
            this.spin(true);
        }
    },
    
    onSpinUp: function() {
        if (!this.getDisabled() && !this.getReadOnly()) {
            this.spin(false);
        }
    },
    
    onTouchStart: function(repeater) {
        if (!this.getDisabled() && !this.getReadOnly()) {
            repeater.getEl().addCls(Ext.baseCSSPrefix + 'button-pressed');
        }
    },
    
    onTouchEnd: function(repeater) {
        repeater.getEl().removeCls(Ext.baseCSSPrefix + 'button-pressed');
    },
    
    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;
            }
        }
        me.setValue(value);
        value = me.getValue();
        me.fireEvent('spin', me, value, direction);
        me.fireEvent('spin' + direction, me, value);
    },
    
    updateDisabled: function(disabled) {
        Ext.Component.prototype.updateDisabled.apply(this, arguments);
    },
    
    setDisabled: function() {
        Ext.Component.prototype.setDisabled.apply(this, arguments);
    },
    reset: function() {
        this.setValue(this.getDefaultValue());
    },
    
    
    
    
    
    
    
    
    destroy: function() {
        var me = this;
        Ext.destroy(me.downRepeater, me.upRepeater, me.spinDownButton, me.spinUpButton);
        me.downRepeater = me.upRepeater = me.spinDownButton = me.spinUpButton = null;
        me.callParent();
    }
});


Ext.define('Ext.slider.Toggle', {
    extend: Ext.slider.Slider,
    config: {
        
        baseCls: 'x-toggle',
        
        minValueCls: 'x-toggle-off',
        
        maxValueCls: 'x-toggle-on'
    },
    initialize: function() {
        this.callParent();
        this.on({
            change: 'onChange'
        });
    },
    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);
        }
    },
    setValue: function(newValue, oldValue) {
        this.callParent(arguments);
        this.onChange(this, this.getThumbs()[0], newValue, oldValue);
    },
    setIndexValue: function(index, value, animation) {
        var oldValue = this.getValue()[index];
        this.callParent(arguments);
        var thumb = this.getThumb(index),
            newValue = this.getValue()[index];
        if (oldValue !== newValue) {
            this.fireEvent('change', this, thumb, newValue, oldValue);
        }
    },
    onChange: function(me, thumb, newValue, oldValue) {
        var isOn = newValue > 0,
            onCls = me.getMaxValueCls(),
            offCls = me.getMinValueCls(),
            element = this.element;
        element.addCls(isOn ? onCls : offCls);
        element.removeCls(isOn ? offCls : onCls);
    },
    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.getThumb(0);
        this.setIndexValue(0, newValue, this.getAnimation());
        this.refreshThumbConstraints(thumb);
    }
});


Ext.define('Ext.field.Toggle', {
    extend: Ext.field.SingleSlider,
    xtype: 'togglefield',
    alternateClassName: 'Ext.form.Toggle',
    
    config: {
        
        cls: 'x-toggle-field',
        
        labelAlign: 'left',
        
        activeLabel: null,
        
        inactiveLabel: null,
        
        value: false
    },
    
    
    
    
    proxyConfig: {
        
        minValueCls: Ext.baseCSSPrefix + 'toggle-off',
        
        maxValueCls: Ext.baseCSSPrefix + 'toggle-on'
    },
    
    applyComponent: function(config) {
        return Ext.factory(config, Ext.slider.Toggle);
    },
    
    updateActiveLabel: function(newActiveLabel, oldActiveLabel) {
        this.getComponent().element.dom.setAttribute('data-activelabel', newActiveLabel);
    },
    
    updateInactiveLabel: function(newInactiveLabel, oldInactiveLabel) {
        this.getComponent().element.dom.setAttribute('data-inactivelabel', newInactiveLabel);
    },
    applyValue: function(value) {
        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
        ]);
    },
    setComponentValue: function(value) {
        this.getComponent().setValue(value ? 1 : 0);
    },
    
    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,
        
        component: {
            type: 'url'
        }
    }
});


Ext.define('Ext.form.FieldSet', {
    extend: Ext.Container,
    alias: 'widget.fieldset',
    config: {
        
        baseCls: Ext.baseCSSPrefix + 'form-fieldset',
        
        title: null,
        
        instructions: null
    },
    
    applyTitle: function(title) {
        if (typeof title == 'string') {
            title = {
                title: title
            };
        }
        Ext.applyIf(title, {
            docked: 'top',
            baseCls: this.getBaseCls() + '-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',
            baseCls: this.getBaseCls() + '-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.getFieldsAsArray().forEach(function(field) {
            field.setDisabled(newDisabled);
        });
        return this;
    },
    
    getFieldsAsArray: function() {
        var fields = [],
            getFieldsFrom = function(item) {
                if (item.isField) {
                    fields.push(item);
                }
                if (item.isContainer) {
                    item.getItems().each(getFieldsFrom);
                }
            };
        this.getItems().each(getFieldsFrom);
        return fields;
    }
});


Ext.define('Ext.form.Panel', {
    alternateClassName: 'Ext.form.FormPanel',
    extend: Ext.Panel,
    xtype: 'formpanel',
    
    
    
    config: {
        
        baseCls: Ext.baseCSSPrefix + 'form',
        
        standardSubmit: false,
        
        url: null,
        
        enctype: null,
        
        baseParams: null,
        
        submitOnAction: false,
        
        record: null,
        
        method: 'post',
        
        scrollable: true,
        
        trackResetOnLoad: false,
        
        api: null,
        
        paramOrder: null,
        
        paramsAsHash: null,
        
        timeout: 30,
        
        multipartDetection: true,
        
        enableSubmissionForm: true
    },
    getElementConfig: function() {
        var config = this.callParent();
        config.tag = "form";
        
        config.children.push({
            tag: 'input',
            type: 'submit',
            style: 'visibility: hidden; width: 0; height: 0; position: absolute; right: 0; bottom: 0;'
        });
        return config;
    },
    
    initialize: function() {
        var me = this;
        me.callParent();
        me.element.on({
            submit: 'onSubmit',
            scope: me
        });
    },
    applyEnctype: function(newValue) {
        var form = this.element.dom || null;
        if (form) {
            if (newValue) {
                form.setAttribute("enctype", newValue);
            } else {
                form.setAttribute("enctype");
            }
        }
    },
    updateRecord: function(newRecord) {
        var fields, values, name;
        if (newRecord) {
            values = this.getValues();
            for (name in values) {
                if (values.hasOwnProperty(name) && newRecord.getField(name)) {
                    newRecord.set(name, values[name]);
                }
            }
        }
        return this;
    },
    
    setRecord: function(record) {
        var me = this;
        if (record && record.data) {
            me.setValues(record.data);
        }
        me._record = record;
        return this;
    },
    
    onSubmit: function(e) {
        var me = this;
        if (e && !me.getStandardSubmit()) {
            e.stopEvent();
        } else {
            this.submit(null, e);
        }
    },
    updateSubmitOnAction: function(newSubmitOnAction) {
        if (newSubmitOnAction) {
            this.on({
                action: 'onFieldAction',
                scope: this
            });
        } else {
            this.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');
    },
    createSubmissionForm: function(form, values) {
        var fields = this.getFields(),
            name, input, field, fileinputElement, inputComponent;
        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) {
                    if (!form.$fileswap)  {
                        form.$fileswap = [];
                    }
                    
                    inputComponent = field.getComponent().input;
                    fileinputElement = inputComponent.dom;
                    input = fileinputElement.cloneNode(true);
                    fileinputElement.parentNode.insertBefore(input, fileinputElement.nextSibling);
                    form.appendChild(fileinputElement);
                    form.$fileswap.push({
                        original: fileinputElement,
                        placeholder: input
                    });
                } else if (field.isPassword) {
                    if (field.getComponent().getType !== "password") {
                        field.setRevealed(false);
                    }
                }
            }
        }
        return form;
    },
    doBeforeSubmit: function(me, formValues, options) {
        var form = options.form || {},
            multipartDetected = false;
        if (this.getMultipartDetection() === true) {
            this.getFieldsAsArray().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()) {
            if (options.url && Ext.isEmpty(form.action)) {
                form.action = options.url;
            }
            
            
            var fields = this.query('spinnerfield'),
                ln = fields.length,
                i, field;
            for (i = 0; i < ln; i++) {
                field = fields[i];
                if (!field.getDisabled()) {
                    field.getComponent().setDisabled(false);
                }
            }
            form.method = (options.method || form.method).toLowerCase();
            form.submit();
        } else {
            var api = me.getApi(),
                url = options.url || me.getUrl(),
                scope = options.scope || me,
                waitMsg = options.waitMsg,
                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);
                },
                submit;
            if (options.waitMsg) {
                if (typeof waitMsg === 'string') {
                    waitMsg = {
                        xtype: 'loadmask',
                        message: waitMsg
                    };
                }
                me.setMasked(waitMsg);
            }
            if (api) {
                submit = api.submit;
                if (typeof submit === 'string') {
                    submit = Ext.direct.Manager.parseMethod(submit);
                    if (submit) {
                        api.submit = submit;
                    }
                }
                if (submit) {
                    return submit(this.element, function(data, response, success) {
                        me.setMasked(false);
                        if (success) {
                            if (data.success) {
                                successFn(response, data);
                            } else {
                                failureFn(response, data);
                            }
                        } else {
                            failureFn(response, data);
                        }
                    }, this);
                }
            } else {
                var request = Ext.merge({}, {
                        url: url,
                        timeout: this.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 me = this,
                        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 = 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;
                    }
                    request.data = formData;
                }
                return Ext.Ajax.request(request);
            }
        }
    },
    
    load: function(options) {
        options = options || {};
        var me = this,
            api = me.getApi(),
            url = me.getUrl() || options.url,
            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(scope, me, response, data);
                }
                me.fireEvent('exception', me, response);
            },
            load, method, args;
        if (options.waitMsg) {
            if (typeof waitMsg === 'string') {
                waitMsg = {
                    xtype: 'loadmask',
                    message: waitMsg
                };
            }
            me.setMasked(waitMsg);
        }
        if (api) {
            load = api.load;
            if (typeof load === 'string') {
                load = Ext.direct.Manager.parseMethod(load);
                if (load) {
                    api.load = load;
                }
            }
            if (load) {
                method = load.directCfg.method;
                args = method.getArgs(me.getParams(options.params), me.getParamOrder(), me.getParamsAsHash());
                args.push(function(data, response, success) {
                    me.setMasked(false);
                    if (success) {
                        successFn(response, data);
                    } else {
                        failureFn(response, data);
                    }
                }, me);
                return load.apply(window, args);
            }
        } else if (url) {
            return Ext.Ajax.request({
                url: url,
                timeout: (options.timeout || this.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());
    },
    
    setValues: function(values) {
        var fields = this.getFields(),
            me = this,
            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 (me.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 (!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() {
        this.getFieldsAsArray().forEach(function(field) {
            field.reset();
        });
        return this;
    },
    
    updateDisabled: function(newDisabled) {
        this.getFieldsAsArray().forEach(function(field) {
            field.setDisabled(newDisabled);
        });
        return this;
    },
    
    getFieldsAsArray: function() {
        var fields = [],
            getFieldsFrom = function(item) {
                if (item.isField) {
                    fields.push(item);
                }
                if (item.isContainer) {
                    item.getItems().each(getFieldsFrom);
                }
            };
        this.getItems().each(getFieldsFrom);
        return fields;
    },
    
    getFields: function(byName) {
        var fields = {},
            itemName;
        var getFieldsFrom = function(item) {
                if (item.isField) {
                    itemName = item.getName();
                    if ((byName && itemName == byName) || typeof byName == 'undefined') {
                        if (fields.hasOwnProperty(itemName)) {
                            if (!Ext.isArray(fields[itemName])) {
                                fields[itemName] = [
                                    fields[itemName]
                                ];
                            }
                            fields[itemName].push(item);
                        } else {
                            fields[itemName] = item;
                        }
                    }
                }
                if (item.isContainer) {
                    item.items.each(getFieldsFrom);
                }
            };
        this.getItems().each(getFieldsFrom);
        return (byName) ? (fields[byName] || []) : fields;
    },
    
    getFieldsArray: function() {
        var fields = [];
        var getFieldsFrom = function(item) {
                if (item.isField) {
                    fields.push(item);
                }
                if (item.isContainer) {
                    item.items.each(getFieldsFrom);
                }
            };
        this.items.each(getFieldsFrom);
        return fields;
    },
    getFieldsFromItem: Ext.emptyFn,
    
    showMask: function(cfg, target) {
        
        Ext.Logger.warn('showMask is now deprecated. Please use Ext.form.Panel#setMasked instead');
        
        cfg = Ext.isObject(cfg) ? cfg.message : cfg;
        if (cfg) {
            this.setMasked({
                xtype: 'loadmask',
                message: cfg
            });
        } else {
            this.setMasked(true);
        }
        return this;
    },
    
    hideMask: function() {
        this.setMasked(false);
        return this;
    },
    
    getFocusedField: function() {
        var fields = this.getFieldsArray(),
            ln = fields.length,
            field, i;
        for (i = 0; i < ln; i++) {
            field = fields[i];
            if (field.isFocused) {
                return field;
            }
        }
        return null;
    },
    
    getNextField: function() {
        var fields = this.getFieldsArray(),
            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.getFieldsArray(),
            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.grid.cell.Base', {
    extend: Ext.Widget,
    cachedConfig: {
        
        align: 'left',
        
        cls: null,
        
        hidden: false,
        
        innerCls: null
    },
    config: {
        
        column: null,
        
        record: null,
        
        value: null
    },
    element: {
        reference: 'element',
        cls: Ext.baseCSSPrefix + 'grid-cell',
        children: [
            {
                reference: 'innerElement',
                cls: Ext.baseCSSPrefix + 'grid-cell-inner'
            }
        ]
    },
    defaultBindProperty: 'value',
    hiddenCls: Ext.baseCSSPrefix + 'grid-cell-hidden',
    getComputedWidth: function() {
        return this.getHidden() ? 0 : this.getWidth();
    },
    updateAlign: function(align, oldAlign) {
        var prefix = Ext.baseCSSPrefix + 'grid-cell-align-';
        this.element.replaceCls(prefix + oldAlign, prefix + align);
    },
    updateCls: function(cls, oldCls) {
        this.element.replaceCls(oldCls, cls);
    },
    updateInnerCls: function(cellCls, oldCellCls) {
        if (cellCls || oldCellCls) {
            this.innerElement.replaceCls(oldCellCls, cellCls);
        }
    },
    updateColumn: function(column) {
        this.dataIndex = column ? column.getDataIndex() : null;
    },
    applyHidden: function(hidden) {
        return Boolean(hidden);
    },
    updateHidden: function(hidden) {
        this.element.toggleCls(this.hiddenCls, hidden);
    },
    updateRecord: function(record) {
        var dataIndex = this.dataIndex;
        if (record && dataIndex) {
            this.setValue(record.get(dataIndex));
        }
    },
    destroy: function() {
        this.setColumn(null);
        this.setRecord(null);
        this.callParent();
    }
});


Ext.define('Ext.grid.cell.Text', {
    extend: Ext.grid.cell.Base,
    xtype: 'textcell',
    config: {
        
        encodeHtml: true,
        
        rawValue: null
    },
    updateRawValue: function(rawValue) {
        var dom = this.innerElement.dom;
        if (this.getEncodeHtml()) {
            dom.textContent = rawValue;
        } else {
            dom.innerHTML = rawValue;
        }
    },
    updateValue: function() {
        this.writeValue();
    },
    writeValue: function() {
        this.setRawValue(this.getValue());
    }
});


Ext.define('Ext.grid.cell.Cell', {
    extend: Ext.grid.cell.Text,
    xtype: 'gridcell',
    updateRecord: function(record, oldRecord) {
        var me = this,
            column = me.getColumn(),
            dataIndex, tpl, renderer, raw, scope, setRaw, value;
        if (record && column) {
            tpl = column.getTpl();
            renderer = column.getRenderer();
            if (tpl) {
                raw = tpl.apply(record.getData(true));
                setRaw = true;
            } else if (renderer) {
                dataIndex = me.dataIndex;
                value = dataIndex ? record.get(dataIndex) : undefined;
                scope = column.getScope();
                if (typeof renderer === 'function') {
                    raw = renderer.call(scope || column, value, record, dataIndex, me, column);
                } else {
                    raw = Ext.callback(renderer, scope, [
                        value,
                        record,
                        dataIndex,
                        me,
                        column
                    ], 0, me);
                }
                setRaw = true;
            }
            if (setRaw) {
                me.setRawValue(raw);
                return;
            }
        }
        me.callParent([
            record,
            oldRecord
        ]);
    }
});


Ext.define('Ext.grid.Row', {
    extend: Ext.Component,
    xtype: 'gridrow',
    mixins: [
        Ext.mixin.Queryable
    ],
    config: {
        baseCls: Ext.baseCSSPrefix + 'grid-row',
        header: {
            xtype: 'component',
            cls: 'x-grid-header',
            html: ' '
        },
        grid: null
    },
    constructor: function(config) {
        this.cells = [];
        this.columnMap = {};
        this.callParent([
            config
        ]);
    },
    applyHeader: function(header) {
        if (header && !header.isComponent) {
            header = Ext.factory(header, Ext.Component, this.getHeader());
        }
        return header;
    },
    updateHeader: function(header, oldHeader) {
        if (oldHeader) {
            oldHeader.destroy();
        }
    },
    updateGrid: function(grid) {
        var me = this,
            i, columns, ln;
        if (grid) {
            columns = grid.getColumns();
            for (i = 0 , ln = columns.length; i < ln; i++) {
                me.addColumn(columns[i]);
            }
        }
    },
    addColumn: function(column) {
        this.insertColumn(this.cells.length, column);
    },
    getRefItems: function() {
        return this.cells;
    },
    insertColumn: function(index, column) {
        var me = this,
            cells = me.cells,
            cell = me.createCell(column);
        if (index === cells.length) {
            me.element.appendChild(cell.element);
            cells.push(cell);
        } else {
            cell.element.insertBefore(cells[index].element);
            cells.splice(index, 0, cell);
        }
        me.columnMap[column.getId()] = cell;
    },
    moveColumn: function(column, fromIdx, toIdx) {
        var cells = this.cells,
            cell = cells[fromIdx];
        Ext.Array.move(cells, fromIdx, toIdx);
        if (toIdx === cells.length - 1) {
            this.element.appendChild(cell.element);
        } else {
            cell.element.insertBefore(cells[toIdx + 1].element);
        }
    },
    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 (!record) {
            return;
        }
        var cells = this.cells,
            len = cells.length,
            i, cell;
        for (i = 0; i < len; ++i) {
            cell = cells[i];
            if (cell.getRecord() === record) {
                cell.updateRecord(record);
            } else {
                cell.setRecord(record);
            }
        }
    },
    setColumnWidth: function(column, width) {
        var cell = this.getCellByColumn(column);
        if (cell) {
            cell.setWidth(width);
        }
    },
    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();
    },
    destroy: function() {
        var me = this;
        me.cells = Ext.destroy(me.cells, me.getHeader());
        me.setRecord(null);
        me.callParent();
    },
    privates: {
        createCell: function(column) {
            var cell = this.getCellCfg(column);
            cell.$initParent = this;
            cell = Ext.create(cell);
            delete cell.$initParent;
            return cell;
        },
        getCellCfg: function(column) {
            return Ext.apply({
                parent: this,
                column: column,
                record: this.getRecord(),
                hidden: column.getHidden(),
                width: column.getComputedWidth()
            }, column.getCell());
        },
        setCellHidden: function(column, hidden) {
            var cell = this.getCellByColumn(column);
            if (cell) {
                cell.setHidden(hidden);
            }
        }
    }
});


Ext.define('Ext.grid.column.Column', {
    extend: Ext.Component,
    alternateClassName: 'Ext.grid.column.Template',
    xtype: [
        'column',
        'templatecolumn'
    ],
    config: {
        
        align: 'left',
        
        cell: {
            xtype: 'gridcell'
        },
        
        dataIndex: null,
        
        text: '&nbsp;',
        
        sortable: true,
        
        groupable: true,
        
        resizable: true,
        
        hideable: true,
        
        renderer: false,
        
        scope: null,
        
        editable: false,
        
        editor: null,
        
        defaultEditor: {
            xtype: 'textfield',
            required: true
        },
        
        ignore: false,
        
        summaryType: null,
        
        summaryRenderer: null,
        minWidth: 40,
        baseCls: Ext.baseCSSPrefix + 'grid-column',
        sortedCls: Ext.baseCSSPrefix + 'column-sorted',
        sortDirection: null,
        
        tpl: null,
        
        computedWidth: null
    },
    applyTpl: function(tpl) {
        if (!tpl || !tpl.isXTemplate) {
            tpl = new Ext.XTemplate(tpl);
        }
        return tpl;
    },
    updateAlign: function(align, oldAlign) {
        var prefix = Ext.baseCSSPrefix + 'grid-column-align-';
        if (oldAlign) {
            this.removeCls(prefix + align);
        }
        if (align) {
            this.addCls(prefix + align);
        }
    },
    initialize: function() {
        this.callParent();
        this.element.on({
            tap: 'onColumnTap',
            longpress: 'onColumnLongPress',
            scope: this
        });
    },
    onColumnTap: function(e) {
        this.fireEvent('tap', this, e);
    },
    onColumnLongPress: function(e) {
        this.fireEvent('longpress', this, e);
    },
    updateText: function(text) {
        this.setHtml(text || '&#160;');
    },
    updateWidth: function(width, oldWidth) {
        this.callParent([
            width,
            oldWidth
        ]);
        
        
        if (width !== null) {
            this.setComputedWidth(width);
        }
    },
    updateFlex: function(flex, oldFlex) {
        var me = this,
            listener = me.resizeListener;
        me.callParent([
            flex,
            oldFlex
        ]);
        if (!flex) {
            me.resizeListener = Ext.destroy(listener);
        } else if (!listener) {
            me.resizeListener = me.on('resize', me.onFlexResize, me, {
                destroyable: true
            });
        }
    },
    onFlexResize: function() {
        this.setComputedWidth(this.element.getWidth(false, true));
    },
    getComputedWidth: function() {
        return this.isVisible(true) ? this.callParent() : 0;
    },
    updateComputedWidth: function(computedWidth, oldComputedWidth) {
        this.fireEvent('columnresize', this, computedWidth, oldComputedWidth);
    },
    updateDataIndex: function(dataIndex) {
        var editor = this.getEditor();
        if (editor) {
            editor.name = dataIndex;
        } else {
            this.getDefaultEditor().name = dataIndex;
        }
    },
    updateSortDirection: function(direction, oldDirection) {
        if (!this.getSortable()) {
            return;
        }
        var sortedCls = this.getSortedCls();
        if (oldDirection) {
            this.element.removeCls(sortedCls + '-' + oldDirection.toLowerCase());
        }
        if (direction) {
            this.element.addCls(sortedCls + '-' + direction.toLowerCase());
        }
        this.fireEvent('sort', this, direction, oldDirection);
    },
    destroy: function() {
        this.resizeListener = Ext.destroy(this.resizeListener);
        this.callParent();
    }
});


Ext.define('Ext.grid.cell.Date', {
    extend: Ext.grid.cell.Text,
    xtype: 'datecell',
    config: {
        
        format: ''
    },
    updateColumn: function(column, oldColumn) {
        this.callParent([
            column,
            oldColumn
        ]);
        if (column) {
            var 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();
        }
    },
    writeValue: function() {
        var value = this.getValue();
        this.setRawValue(value ? Ext.Date.format(value, this.getFormat()) : null);
    }
});


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


Ext.define('Ext.grid.HeaderContainer', {
    extend: Ext.Container,
    xtype: 'headercontainer',
    config: {
        baseCls: Ext.baseCSSPrefix + 'grid-header-container',
        docked: 'top',
        defaultType: 'column',
        layout: {
            type: 'hbox',
            align: 'stretch'
        },
        
        sortable: true,
        scrollable: {
            autoRefresh: null,
            x: false,
            y: false
        },
        grid: null
    },
    initialize: function() {
        var me = this;
        me.columns = [];
        me.callParent();
        me.on({
            tap: 'onHeaderTap',
            columnresize: 'onColumnResize',
            show: 'onColumnShow',
            hide: 'onColumnHide',
            sort: 'onColumnSort',
            scope: me,
            delegate: 'column'
        });
        me.on({
            show: 'onGroupShow',
            hide: 'onGroupHide',
            add: 'onColumnAdd',
            move: 'onColumnMove',
            remove: 'onColumnRemove',
            scope: me,
            delegate: 'gridheadergroup'
        });
        me.on({
            add: 'onColumnAdd',
            move: 'onColumnMove',
            remove: 'onColumnRemove',
            scope: me
        });
    },
    factoryItem: function(item) {
        
        if (item.columns) {
            return Ext.factory(item, Ext.grid.HeaderGroup);
        }
        return this.callParent([
            item
        ]);
    },
    getColumns: function() {
        return this.columns;
    },
    getAbsoluteColumnIndex: function(column) {
        var items = this.getInnerItems(),
            ln = items.length,
            index = 0,
            innerIndex, i, item;
        for (i = 0; i < ln; i++) {
            item = items[i];
            if (item === column) {
                return index;
            } else if (item.isHeaderGroup) {
                innerIndex = item.innerIndexOf(column);
                if (innerIndex !== -1) {
                    index += innerIndex;
                    return index;
                } else {
                    index += item.getInnerItems().length;
                }
            } else {
                index += 1;
            }
        }
    },
    onColumnAdd: function(parent, column) {
        var me = this,
            columns = me.columns,
            columnIndex = me.getAbsoluteColumnIndex(column),
            groupColumns, ln, i;
        if (column.isHeaderGroup) {
            groupColumns = column.getItems().items;
            for (i = 0 , ln = groupColumns.length; i < ln; i++) {
                columns.splice(columnIndex + i, 0, groupColumns[i]);
                me.fireEvent('columnadd', me, groupColumns[i], column);
            }
        } else {
            columns.splice(columnIndex, 0, column);
            me.fireEvent('columnadd', me, column, null);
        }
    },
    onColumnMove: function(parent, column) {
        var me = this,
            columns = me.columns,
            columnIndex = me.getAbsoluteColumnIndex(column),
            groupColumns, ln, i, groupColumn, after, oldIndex, fromIdx, toIdx;
        if (column.isHeaderGroup) {
            groupColumns = column.getItems().items;
            for (i = 0 , ln = groupColumns.length; i < ln; i++) {
                groupColumn = groupColumns[i];
                if (i === 0) {
                    oldIndex = columns.indexOf(groupColumn);
                    after = oldIndex - columnIndex < 0;
                }
                
                if (after) {
                    
                    
                    
                    
                    
                    
                    toIdx = columnIndex + ln - 1;
                    fromIdx = oldIndex;
                } else {
                    
                    
                    
                    
                    
                    
                    fromIdx = oldIndex + i;
                    toIdx = columnIndex + i;
                }
                Ext.Array.move(columns, fromIdx, toIdx);
                me.fireEvent('columnmove', me, groupColumn, column, fromIdx, toIdx);
            }
        } else {
            fromIdx = columns.indexOf(column);
            toIdx = columnIndex;
            Ext.Array.move(columns, fromIdx, toIdx);
            me.fireEvent('columnmove', me, column, null, fromIdx, toIdx);
        }
    },
    onColumnRemove: function(parent, column) {
        if (column.isHeaderGroup) {
            var columns = column.getItems().items,
                ln = columns.length,
                i;
            for (i = 0; i < ln; i++) {
                Ext.Array.remove(this.columns, columns[i]);
                this.fireEvent('columnremove', this, columns[i]);
            }
        } else {
            Ext.Array.remove(this.columns, column);
            this.fireEvent('columnremove', this, column);
        }
    },
    onHeaderTap: function(column) {
        if (this.getSortable() && !column.getIgnore() && column.getSortable()) {
            var sortDirection = column.getSortDirection() || 'DESC',
                newDirection = (sortDirection === 'DESC') ? 'ASC' : 'DESC';
            column.setSortDirection(newDirection);
        }
        this.fireEvent('columntap', this, column);
    },
    onColumnShow: function(column) {
        this.fireEvent('columnshow', this, column);
    },
    onColumnHide: function(column) {
        this.fireEvent('columnhide', this, column);
    },
    onGroupShow: function(group) {
        var columns = group.getInnerItems(),
            ln = columns.length,
            i, column;
        for (i = 0; i < ln; i++) {
            column = columns[i];
            if (!column.isHidden()) {
                this.fireEvent('columnshow', this, column);
            }
        }
    },
    onGroupHide: function(group) {
        var columns = group.getInnerItems(),
            ln = columns.length,
            i, column;
        for (i = 0; i < ln; i++) {
            column = columns[i];
            this.fireEvent('columnhide', this, column);
        }
    },
    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);
    },
    setTotalWidth: function(totalWidth) {
        var scrollable = this.getScrollable(),
            innerElement;
        if (scrollable && scrollable.isTouchScroller) {
            innerElement = scrollable.getInnerElement();
            if (innerElement) {
                innerElement.setWidth(totalWidth);
                scrollable.setSize({
                    x: totalWidth,
                    y: scrollable.getSize().y
                });
            }
        }
    },
    destroy: function() {
        var me = this,
            task = me.spacerTask;
        if (task) {
            task.cancel();
            me.spacerTask = null;
        }
        me.setGrid(null);
        me.callParent();
    },
    privates: {
        updateSpacer: function() {
            var me = this,
                task = me.spacerTask;
            if (!task) {
                me.spacerTask = task = new Ext.util.DelayedTask(me.doUpdateSpacer, me);
            }
            task.delay(1);
        },
        doUpdateSpacer: function() {
            var scrollable = this.getGrid().getScrollable();
            this.element.setStyle('padding-right', scrollable.getScrollbarSize().width + 'px');
        }
    }
});


Ext.define('Ext.grid.HeaderGroup', {
    extend: Ext.Container,
    alias: 'widget.gridheadergroup',
    isHeaderGroup: true,
    config: {
        
        text: '&nbsp;',
        
        columns: null,
        
        
        items: undefined,
        defaultType: 'column',
        baseCls: Ext.baseCSSPrefix + 'grid-headergroup',
        
        hidden: true,
        layout: {
            type: 'hbox',
            align: 'stretch'
        }
    },
    getElementConfig: function() {
        return {
            reference: 'element',
            classList: [
                'x-container',
                'x-unsized'
            ],
            children: [
                {
                    reference: 'textElement',
                    className: 'x-grid-headergroup-text'
                },
                {
                    reference: 'innerElement',
                    className: 'x-inner'
                }
            ]
        };
    },
    applyItems: function(items, collection) {
        if (!items) {
            items = this.getColumns();
        }
        this.callParent([
            items,
            collection
        ]);
    },
    updateText: function(text) {
        this.textElement.setHtml(text);
    },
    initialize: function() {
        var me = this;
        me.on({
            add: 'doVisibilityCheck',
            remove: 'doVisibilityCheck',
            show: 'onColumnShow',
            hide: 'onColumnHide',
            delegate: '> column',
            scope: 'this'
        });
        me.on({
            show: 'onShow',
            scope: 'this'
        });
        me.callParent();
        me.doVisibilityCheck();
    },
    onColumnShow: function(column) {
        if (this.getVisibleCount() === this.getInnerItems().length) {
            this.show();
        }
    },
    onColumnHide: function(column) {
        if (this.getVisibleCount() === 0) {
            this.hide();
        }
    },
    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();
    },
    destroy: function() {
        this.setColumns(null);
        this.callParent();
    },
    privates: {
        getVisibleCount: function() {
            var columns = this.getInnerItems(),
                len = columns.length,
                count = 0,
                i;
            for (i = 0; i < len; ++i) {
                count += columns[i].isHidden() ? 0 : 1;
            }
            return count;
        }
    }
});


Ext.define('Ext.grid.Grid', {
    extend: Ext.dataview.List,
    xtype: 'grid',
    config: {
        defaultType: 'gridrow',
        
        infinite: true,
        
        columns: null,
        
        baseCls: Ext.baseCSSPrefix + 'grid',
        
        variableHeights: false,
        headerContainer: {
            xtype: 'headercontainer'
        },
        
        hideHeaders: false,
        
        striped: true,
        itemCls: Ext.baseCSSPrefix + 'list-item',
        scrollToTopOnRefresh: false,
        titleBar: {
            xtype: 'titlebar',
            docked: 'top'
        },
        
        title: ''
    },
    
    
    
    
    
    
    
    initialize: function() {
        var me = this,
            titleBar = me.getTitleBar(),
            headerContainer = me.getHeaderContainer(),
            scrollable = me.getScrollable(),
            container;
        me.callParent();
        if (scrollable) {
            headerContainer.getScrollable().addPartner(scrollable, 'x');
        }
        container = me.container;
        if (titleBar) {
            container.add(me.getTitleBar());
        }
        container.add(headerContainer);
        me.scrollElement.addCls(Ext.baseCSSPrefix + 'grid-scrollelement');
    },
    applyTitleBar: function(titleBar) {
        if (titleBar && !titleBar.isComponent) {
            titleBar = Ext.factory(titleBar, Ext.TitleBar);
        }
        return titleBar;
    },
    updateTitle: function(title) {
        var titleBar = this.getTitleBar();
        if (titleBar) {
            if (title) {
                titleBar.setTitle(title);
            } else {
                titleBar.hide();
            }
        }
    },
    applyHeaderContainer: function(headerContainer) {
        if (headerContainer && !headerContainer.isComponent) {
            headerContainer = Ext.factory(headerContainer, Ext.grid.HeaderContainer);
        }
        return headerContainer;
    },
    updateHeaderContainer: function(headerContainer, oldHeaderContainer) {
        var me = this;
        if (oldHeaderContainer) {
            oldHeaderContainer.un({
                columnsort: 'onColumnSort',
                columnresize: 'onColumnResize',
                columnshow: 'onColumnShow',
                columnhide: 'onColumnHide',
                columnadd: 'onColumnAdd',
                columnmove: 'onColumnMove',
                columnremove: 'onColumnRemove',
                scope: me
            });
        }
        if (headerContainer) {
            headerContainer.on({
                columnsort: 'onColumnSort',
                columnresize: 'onColumnResize',
                columnshow: 'onColumnShow',
                columnhide: 'onColumnHide',
                columnadd: 'onColumnAdd',
                columnmove: 'onColumnMove',
                columnremove: 'onColumnRemove',
                scope: me
            });
            headerContainer.setGrid(me);
        }
    },
    updateHideHeaders: function(hideHeaders) {
        var ct = this.getHeaderContainer(),
            oldCtHeight = this.oldCtHeight || null;
        
        if (!hideHeaders && ct.getHeight() !== 0) {
            return;
        }
        
        
        if (hideHeaders) {
            this.oldCtHeight = ct.getHeight();
        }
        ct.setHeight(hideHeaders ? 0 : oldCtHeight);
    },
    addColumn: function(column) {
        return this.getHeaderContainer().add(column);
    },
    removeColumn: function(column) {
        return this.getHeaderContainer().remove(column);
    },
    insertColumn: function(index, column) {
        return this.getHeaderContainer().insert(index, column);
    },
    onColumnAdd: function(container, column) {
        var me = this,
            items, ln, columnIndex, i, row;
        if (me.initialized && !me.destroying) {
            items = this.listItems;
            ln = items.length;
            columnIndex = container.getColumns().indexOf(column);
            for (i = 0; i < ln; i++) {
                row = items[i];
                row.insertColumn(columnIndex, column);
            }
            me.updateTotalColumnWidth();
            me.fireEvent('columnadd', me, column, columnIndex);
        }
    },
    onColumnMove: function(container, column, group, fromIdx, toIdx) {
        var me = this,
            items, ln, i, row;
        if (me.initialized && !me.destroying) {
            items = me.listItems;
            ln = items.length;
            for (i = 0; i < ln; i++) {
                row = items[i];
                row.moveColumn(column, fromIdx, toIdx);
            }
            me.fireEvent('columnmove', me, column, fromIdx, toIdx);
        }
    },
    onColumnRemove: function(container, column) {
        var me = this,
            items, ln, i, row;
        if (me.initialized && !me.destroying) {
            items = me.listItems;
            ln = items.length;
            for (i = 0; i < ln; i++) {
                row = items[i];
                row.removeColumn(column);
            }
            me.updateTotalColumnWidth();
            me.fireEvent('columnremove', me, column);
        }
    },
    updateColumns: function(columns) {
        if (columns && columns.length) {
            var ln = columns.length,
                i;
            for (i = 0; i < ln; i++) {
                this.addColumn(columns[i]);
            }
            this.updateTotalColumnWidth();
        }
    },
    getColumns: function() {
        return this.getHeaderContainer().getColumns();
    },
    onColumnResize: function(container, column, width, oldWidth) {
        var me = this,
            items = me.listItems,
            ln = items.length,
            i, row;
        if (!me.destroying) {
            for (i = 0; i < ln; i++) {
                row = items[i];
                row.setColumnWidth(column, width);
            }
            if (me.initialized) {
                me.updateTotalColumnWidth();
                
                if (oldWidth !== null && !column.getHidden()) {
                    me.fireEvent('columnresize', me, column, width);
                }
            }
        }
    },
    onColumnShow: function(container, column) {
        var me = this,
            items, ln, i, row, w;
        if (me.initialized && !me.destroying) {
            items = me.listItems;
            ln = items.length;
            me.updateTotalColumnWidth();
            if (!column.getFlex()) {
                w = column.getWidth();
            }
            for (i = 0; i < ln; i++) {
                row = items[i];
                row.showColumn(column);
                
                
                if (w !== undefined) {
                    row.setColumnWidth(column, w);
                }
            }
            me.fireEvent('columnshow', me, column);
        }
    },
    onColumnHide: function(container, column) {
        var me = this,
            items, ln, i, row;
        if (me.initialized && !me.destroying) {
            items = me.listItems;
            ln = items.length;
            me.updateTotalColumnWidth();
            for (i = 0; i < ln; i++) {
                row = items[i];
                row.hideColumn(column);
            }
            me.fireEvent('columnhide', me, column);
        }
    },
    onColumnSort: function(container, column, direction) {
        var me = this,
            sorted = me.sortedColumn;
        if (sorted && sorted !== column) {
            sorted.setSortDirection(null);
        }
        me.sortedColumn = column;
        me.getStore().sort(column.getDataIndex(), direction);
        me.fireEvent('columnsort', me, column, direction);
    },
    refreshScroller: function(skipOnRefresh) {
        this.callParent([
            skipOnRefresh
        ]);
        this.getHeaderContainer().updateSpacer();
    },
    getTotalColumnWidth: function() {
        var me = this,
            columns = me.getColumns(),
            ln = columns.length,
            totalWidth = 0,
            i, column, parent;
        for (i = 0; i < ln; i++) {
            column = columns[i];
            parent = column.getParent();
            if (!column.isHidden() && (!parent.isHeaderGroup || !parent.isHidden())) {
                totalWidth += column.getComputedWidth();
            }
        }
        return totalWidth;
    },
    updateTotalColumnWidth: function() {
        var me = this,
            scroller = me.getScrollable(),
            totalWidth = this.getTotalColumnWidth(),
            header = me.getHeaderContainer();
        me.scrollElement.setWidth(totalWidth);
        header.setTotalWidth(totalWidth);
        header.updateSpacer();
        scroller.setSize({
            x: totalWidth,
            y: scroller.getSize().y
        });
    },
    createItem: function(config) {
        config.grid = this;
        return this.callParent([
            config
        ]);
    },
    destroy: function() {
        this.destroying = true;
        this.callParent();
        this.destroying = false;
    }
});


Ext.define('Ext.grid.cell.Boolean', {
    extend: Ext.grid.cell.Text,
    xtype: 'booleancell',
    config: {
        
        falseText: 'False',
        
        trueText: 'True',
        
        undefinedText: ''
    },
    updateColumn: function(column, oldColumn) {
        this.callParent([
            column,
            oldColumn
        ]);
        if (column) {
            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();
        }
    },
    writeValue: function() {
        var me = this,
            value = me.getValue();
        if (value === undefined) {
            value = me.getUndefinedText();
        } else if (!value || value === 'false') {
            value = me.getFalseText();
        } else {
            value = me.getTrueText();
        }
        me.setRawValue(value);
    }
});


Ext.define('Ext.grid.cell.Number', {
    extend: Ext.grid.cell.Text,
    xtype: 'numbercell',
    config: {
        
        format: '0,000.00'
    },
    updateColumn: function(column, oldColumn) {
        this.callParent([
            column,
            oldColumn
        ]);
        if (column) {
            var format = column.getFormat();
            if (format !== null) {
                this.setFormat(format);
            }
        }
    },
    updateFormat: function(format) {
        if (!this.isConfiguring) {
            this.writeValue();
        }
    },
    writeValue: function() {
        var value = this.getValue(),
            hasValue = value || value === 0;
        this.setRawValue(hasValue ? Ext.util.Format.number(value, this.getFormat()) : null);
    }
});


Ext.define('Ext.grid.cell.Widget', {
    extend: Ext.grid.cell.Base,
    xtype: 'widgetcell',
    config: {
        
        widget: null
    },
    applyWidget: function(widget) {
        if (widget) {
            var parent = this.getParent();
            if (parent && !parent.isSpecialRow) {
                widget = Ext.apply({
                    parent: this
                }, widget);
                widget = Ext.widget(widget);
            } else {
                widget = undefined;
            }
        }
        return widget;
    },
    updateWidget: function(widget, oldWidget) {
        if (oldWidget) {
            oldWidget.destroy();
        }
        if (widget) {
            this.innerElement.appendChild(widget.element);
        }
    },
    updateValue: function(value) {
        var widget = this.getWidget(),
            defaultBindProperty;
        if (widget) {
            defaultBindProperty = widget.defaultBindProperty;
            if (defaultBindProperty) {
                widget.setConfig(defaultBindProperty, value);
            }
        }
    },
    destroy: function() {
        this.setWidget(null);
        this.callParent();
    }
});


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


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


Ext.define('Ext.grid.plugin.ColumnResizing', {
    extend: Ext.Component,
    alias: 'plugin.gridcolumnresizing',
    config: {
        grid: null
    },
    init: function(grid) {
        this.setGrid(grid);
    },
    updateGrid: function(grid, oldGrid) {
        if (oldGrid) {
            oldGrid.getHeaderContainer().renderElement.un({
                pinchstart: 'onContainerPinchStart',
                pinch: 'onContainerPinch',
                pinchend: 'onContainerPinchEnd',
                scope: this
            });
        }
        if (grid) {
            grid.getHeaderContainer().renderElement.on({
                pinchstart: 'onContainerPinchStart',
                pinch: 'onContainerPinch',
                pinchend: 'onContainerPinchEnd',
                scope: this
            });
        }
    },
    onContainerPinchStart: function(e) {
        var target = e.getTarget('.' + Ext.baseCSSPrefix + 'grid-column'),
            column;
        if (target) {
            column = Ext.getCmp(target.id);
            if (column && column.getResizable()) {
                this.startColumnWidth = column.getWidth();
                this.resizeColumn = column;
                this.startDistance = e.distance;
                column.renderElement.addCls(Ext.baseCSSPrefix + 'grid-column-resizing');
            } else {
                e.preventDefault();
            }
        }
    },
    onContainerPinch: function(e) {
        var column = this.resizeColumn,
            resizeAmount = e.distance - this.startDistance;
        if (column) {
            this.currentColumnWidth = Math.ceil(this.startColumnWidth + resizeAmount);
            column.renderElement.setWidth(this.currentColumnWidth);
        }
    },
    onContainerPinchEnd: function() {
        var column = this.resizeColumn;
        if (column) {
            column.setWidth(this.currentColumnWidth + 1);
            column.renderElement.removeCls(Ext.baseCSSPrefix + 'grid-column-resizing');
            delete this.resizeColumn;
        }
    }
});


Ext.define('Ext.grid.plugin.Editable', {
    extend: Ext.Component,
    alias: 'plugin.grideditable',
    config: {
        
        grid: null,
        
        triggerEvent: 'doubletap',
        
        formConfig: null,
        
        defaultFormConfig: {
            xtype: 'formpanel',
            modal: true,
            scrollable: true,
            items: {
                xtype: 'fieldset'
            }
        },
        
        toolbarConfig: {
            xtype: 'titlebar',
            docked: 'top',
            items: [
                {
                    xtype: 'button',
                    ui: 'decline',
                    text: 'Cancel',
                    align: 'left',
                    action: 'cancel'
                },
                {
                    xtype: 'button',
                    ui: 'confirm',
                    text: 'Submit',
                    align: 'right',
                    action: 'submit'
                }
            ]
        },
        
        enableDeleteButton: true
    },
    init: function(grid) {
        this.setGrid(grid);
    },
    updateGrid: function(grid, oldGrid) {
        var triggerEvent = this.getTriggerEvent();
        if (oldGrid) {
            oldGrid.renderElement.un(triggerEvent, 'onTrigger', this);
        }
        if (grid) {
            grid.renderElement.on(triggerEvent, 'onTrigger', this);
        }
    },
    onCancelTap: function() {
        this.sheet.hide();
    },
    onSubmitTap: function() {
        this.form.getRecord().set(this.form.getValues());
        this.sheet.hide();
    },
    onSheetHide: function() {
        this.sheet.destroy();
        this.form = null;
        this.sheet = null;
    },
    getRecordByTriggerEvent: function(e) {
        var rowEl = e.getTarget('.' + Ext.baseCSSPrefix + 'grid-row'),
            row;
        if (rowEl) {
            row = Ext.getCmp(rowEl.id);
            if (row) {
                return row.getRecord();
            }
        }
        return null;
    },
    getEditorFields: function(columns) {
        var fields = [],
            ln = columns.length,
            i, column, editor;
        for (i = 0; i < ln; i++) {
            column = columns[i];
            if (column.getEditable()) {
                editor = Ext.apply({}, column.getEditor() || column.getDefaultEditor());
                editor.label = column.getText();
                fields.push(editor);
            }
        }
        return fields;
    },
    onTrigger: function(e) {
        var me = this,
            grid = me.getGrid(),
            formConfig = me.getFormConfig(),
            toolbarConfig = me.getToolbarConfig(),
            record = me.getRecordByTriggerEvent(e),
            fields, form, sheet, toolbar;
        if (record) {
            if (formConfig) {
                this.form = form = Ext.factory(formConfig, Ext.form.Panel);
            } else {
                this.form = form = Ext.factory(me.getDefaultFormConfig());
                fields = me.getEditorFields(grid.getColumns());
                form.down('fieldset').setItems(fields);
            }
            form.setRecord(record);
            toolbar = Ext.factory(toolbarConfig, Ext.form.TitleBar);
            toolbar.down('button[action=cancel]').on('tap', 'onCancelTap', this);
            toolbar.down('button[action=submit]').on('tap', 'onSubmitTap', this);
            this.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', this);
            sheet.show();
        }
    }
});


Ext.define('Ext.grid.plugin.MultiSelection', {
    extend: Ext.Component,
    alias: 'plugin.gridmultiselection',
    config: {
        
        grid: null,
        
        selectionColumn: {
            width: 60,
            xtype: 'column',
            cls: Ext.baseCSSPrefix + 'grid-multiselection-column',
            cell: {
                cls: Ext.baseCSSPrefix + 'grid-multiselection-cell'
            },
            ignore: true,
            hidden: true
        },
        
        useTriggerButton: true,
        
        triggerText: 'Select',
        
        cancelText: 'Cancel',
        
        deleteText: 'Delete'
    },
    init: function(grid) {
        this.setGrid(grid);
        var titleBar = grid.getTitleBar();
        if (this.getUseTriggerButton() && titleBar) {
            this.triggerButton = titleBar.add({
                align: 'right',
                xtype: 'button',
                text: this.getTriggerText()
            });
            this.triggerButton.on({
                tap: 'onTriggerButtonTap',
                scope: this
            });
        }
        grid.getHeaderContainer().on({
            columntap: 'onColumnTap',
            scope: this
        });
    },
    onTriggerButtonTap: function() {
        if (this.getSelectionColumn().isHidden()) {
            this.enterSelectionMode();
        } else {
            this.deleteSelectedRecords();
            this.getGrid().deselectAll();
        }
    },
    onColumnTap: function(container, column) {
        var grid = this.getGrid();
        if (column === this.getSelectionColumn()) {
            if (grid.getSelectionCount() === grid.getStore().getCount()) {
                grid.deselectAll();
            } else {
                grid.selectAll();
            }
        }
    },
    enterSelectionMode: function() {
        this.triggerButton.setText(this.getDeleteText());
        this.triggerButton.setUi('decline');
        this.cancelButton = this.getGrid().getTitleBar().add({
            align: 'right',
            xtype: 'button',
            ui: 'action',
            text: this.getCancelText(),
            scope: this
        });
        this.cancelButton.on({
            tap: 'exitSelectionMode',
            scope: this
        });
        this.getSelectionColumn().show();
        this.getGrid().setMode('MULTI');
    },
    exitSelectionMode: function() {
        this.cancelButton.destroy();
        this.triggerButton.setText(this.getTriggerText());
        this.triggerButton.setUi(null);
        this.getSelectionColumn().hide();
        this.getGrid().setMode('SINGLE');
        this.getGrid().deselectAll();
    },
    deleteSelectedRecords: function() {
        this.getGrid().getStore().remove(this.getGrid().getSelection());
    },
    applySelectionColumn: function(column) {
        if (column && !column.isComponent) {
            column = Ext.factory(column, Ext.grid.Column);
        }
        return column;
    },
    updateSelectionColumn: function(column, oldColumn) {
        var grid = this.getGrid();
        if (grid) {
            if (oldColumn) {
                grid.removeColumn(oldColumn);
            }
            if (column) {
                grid.insertColumn(0, column);
            }
        }
    },
    onGridSelectionChange: function() {
        var grid = this.getGrid(),
            column = this.getSelectionColumn();
        if (grid.getSelectionCount() === grid.getStore().getCount()) {
            column.addCls(Ext.baseCSSPrefix + 'grid-multiselection-allselected');
        } else {
            column.removeCls(Ext.baseCSSPrefix + 'grid-multiselection-allselected');
        }
    },
    updateGrid: function(grid, oldGrid) {
        var delegateCls = '.' + Ext.baseCSSPrefix + 'grid-multiselectioncell';
        if (oldGrid) {
            oldGrid.removeColumn(this.getSelectionColumn());
            oldGrid.un({
                selectionchange: 'onGridSelectionChange',
                scope: this
            });
        }
        if (grid) {
            grid.insertColumn(0, this.getSelectionColumn());
            grid.on({
                selectionchange: 'onGridSelectionChange',
                scope: this
            });
        }
    }
});


Ext.define('Ext.grid.plugin.PagingToolbar', {
    extend: Ext.Component,
    alias: 'plugin.gridpagingtoolbar',
    mixins: [
        Ext.mixin.Hookable
    ],
    config: {
        grid: null,
        currentPage: 1,
        totalPages: 0,
        pageSize: 0,
        totalCount: 0,
        toolbar: {
            xtype: 'toolbar',
            docked: 'bottom',
            ui: 'gray',
            cls: Ext.baseCSSPrefix + 'grid-pagingtoolbar',
            items: [
                {
                    xtype: 'button',
                    ui: 'plain',
                    iconCls: Ext.baseCSSPrefix + 'grid-pagingtoolbar-prev',
                    action: 'previouspage'
                },
                {
                    xtype: 'component',
                    role: 'currentpage',
                    width: 20,
                    cls: Ext.baseCSSPrefix + 'grid-pagingtoolbar-currentpage'
                },
                {
                    xtype: 'component',
                    role: 'totalpages',
                    width: 50,
                    tpl: '&nbsp;/ {totalPages}'
                },
                {
                    xtype: 'singlesliderfield',
                    value: 1,
                    flex: 1,
                    minValue: 1,
                    role: 'pageslider'
                },
                {
                    xtype: 'button',
                    ui: 'plain',
                    iconCls: Ext.baseCSSPrefix + 'grid-pagingtoolbar-next',
                    action: 'nextpage'
                }
            ]
        }
    },
    init: function(grid) {
        var me = this;
        me.setGrid(grid);
        grid.container.add(me.getToolbar());
        if (grid.getStore().getCount()) {
            me.updatePageInfo(me.getCurrentPage());
        }
    },
    updateGrid: function(grid, oldGrid) {
        var me = this;
        if (oldGrid) {
            oldGrid.un({
                updatevisiblecount: 'onUpdateVisibleCount',
                scope: me
            });
            oldGrid.getStore().un({
                add: 'onTotalCountChange',
                remove: 'onTotalCountChange',
                refresh: 'onTotalCountChange',
                scope: me
            });
            me.unbindHook(grid, 'onScrollBinder', 'checkPageChange');
        }
        if (grid) {
            grid.on({
                updatevisiblecount: 'onUpdateVisibleCount',
                scope: me
            });
            grid.getStore().on({
                add: 'onTotalCountChange',
                remove: 'onTotalCountChange',
                refresh: 'onTotalCountChange',
                clear: 'onTotalCountChange',
                scope: me
            });
            me.bindHook(grid, 'onScrollBinder', 'checkPageChange');
        }
    },
    checkPageChange: function() {
        var me = this,
            grid = me.getGrid(),
            pageSize = me.getPageSize(),
            currentPage = me.getCurrentPage(),
            totalCount = me.getTotalCount(),
            topVisibleIndex = grid.topVisibleIndex,
            newPage = Math.floor(grid.topVisibleIndex / pageSize) + 1;
        if (topVisibleIndex && newPage !== currentPage) {
            me.preventGridScroll = true;
            me.setCurrentPage(newPage);
            me.preventGridScroll = false;
        }
    },
    applyToolbar: function(toolbar) {
        if (toolbar && !toolbar.isComponent) {
            toolbar = Ext.factory(toolbar, Ext.Toolbar);
        }
        return toolbar;
    },
    updateToolbar: function(toolbar) {
        var me = this;
        if (toolbar) {
            me.currentPage = toolbar.down('component[role=currentpage]');
            me.totalPages = toolbar.down('component[role=totalpages]');
            me.pageSlider = toolbar.down('sliderfield[role=pageslider]');
            me.nextPageButton = toolbar.down('button[action=nextpage]');
            me.previousPageButton = toolbar.down('button[action=previouspage]');
            me.pageSlider.on({
                change: 'onPageChange',
                drag: 'onPageSliderDrag',
                scope: me
            });
            me.nextPageButton.on({
                tap: 'onNextPageTap',
                scope: me
            });
            me.previousPageButton.on({
                tap: 'onPreviousPageTap',
                scope: me
            });
            me.currentPage.element.createChild({
                tag: 'span'
            });
        }
    },
    onPageChange: function(field, value) {
        this.setCurrentPage(value);
    },
    onPageSliderDrag: function(field, slider, value) {
        this.setCurrentPage(value);
    },
    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) {
        this.setTotalCount(store.getCount());
    },
    onUpdateVisibleCount: function(grid, visibleCount) {
        visibleCount -= 1;
        var store = grid.getStore(),
            totalCount = store.getCount(),
            totalPages = Math.ceil(totalCount / visibleCount);
        this.setTotalPages(totalPages);
        this.setPageSize(visibleCount);
    },
    updateTotalPages: function(totalPages) {
        var me = this;
        
        me.getToolbar();
        me.totalPages.setData({
            totalPages: totalPages
        });
        me.pageSlider.setMaxValue(totalPages || 1);
        me.updatePageInfo(me.getCurrentPage());
    },
    updateCurrentPage: function(currentPage) {
        this.updatePageInfo(currentPage);
    },
    updateTotalCount: function(totalCount) {
        var totalPages;
        if (totalCount !== null && totalCount !== undefined) {
            if (totalCount === 0) {
                totalPages = 1;
            } else {
                totalPages = Math.ceil(totalCount / this.getPageSize());
            }
            this.setTotalPages(totalPages);
        }
    },
    updatePageButtons: function() {
        var me = this,
            currentPage = me.getCurrentPage();
        me.previousPageButton.setDisabled(currentPage === me.getTotalPages());
        me.nextPageButton.enable(currentPage === 1);
    },
    getPageTopRecord: function(page) {
        var grid = this.getGrid(),
            store = grid && grid.getStore(),
            pageSize = this.getPageSize(),
            pageTopRecordIndex = (page - 1) * pageSize,
            pageTopRecord = store && store.getAt(pageTopRecordIndex);
        return pageTopRecord;
    },
    privates: {
        updatePageInfo: function(currentPage) {
            var me = this,
                grid = me.getGrid(),
                pageTopRecord;
            
            me.getToolbar();
            
            
            me.currentPage.element.dom.lastChild.innerHTML = currentPage;
            me.pageSlider.setValue(currentPage);
            pageTopRecord = me.getPageTopRecord(currentPage);
            if (grid && !me.preventGridScroll && pageTopRecord) {
                grid.scrollToRecord(pageTopRecord);
            }
            me.updatePageButtons();
        }
    }
});


Ext.define('Ext.grid.plugin.SummaryRow', {
    extend: Ext.grid.Row,
    alias: 'plugin.gridsummaryrow',
    mixins: [
        Ext.mixin.Hookable
    ],
    isSpecialRow: true,
    config: {
        grid: null,
        cls: Ext.baseCSSPrefix + 'grid-summaryrow',
        emptyText: '',
        emptyCls: Ext.baseCSSPrefix + 'grid-summaryrow-empty',
        docked: 'top',
        translatable: {
            translationMethod: 'csstransform'
        }
    },
    init: function(grid) {
        this.setGrid(grid);
    },
    updateGrid: function(grid, oldGrid) {
        var me = this,
            columns, len, headerContainer, i;
        me.storeListeners = Ext.destroy(me.storeListeners);
        if (grid) {
            columns = grid.getColumns();
            len = columns.length;
            headerContainer = grid.getHeaderContainer();
            me.storeListeners = grid.getStore().onAfter({
                destroyable: true,
                scope: me,
                add: 'doUpdateSummary',
                remove: 'doUpdateSummary',
                update: 'doUpdateSummary',
                refresh: 'doUpdateSummary',
                clear: 'doUpdateSummary'
            });
            grid.getHeaderContainer().on({
                columnadd: 'onColumnAdd',
                columnmove: 'onColumnMove',
                columnremove: 'onColumnRemove',
                columnshow: 'onColumnShow',
                columnhide: 'onColumnHide',
                columnresize: 'onColumnResize',
                scope: me
            });
            if (grid.initialized) {
                grid.container.insertAfter(me, headerContainer);
            } else {
                grid.on('initialize', function() {
                    grid.container.insertAfter(me, headerContainer);
                }, me, {
                    single: true
                });
            }
            grid.addCls(Ext.baseCSSPrefix + 'grid-hassummaryrow');
            for (i = 0; i < len; i++) {
                me.onColumnAdd(headerContainer, columns[i]);
            }
            me.bindHook(grid, 'onScrollBinder', 'onGridScroll');
        }
    },
    onGridScroll: function(x) {
        if (this.currentX !== x) {
            this.translate(x);
            this.currentX = x;
        }
    },
    onColumnAdd: function(container, column) {
        this.insertColumn(container.getColumns().indexOf(column), column);
        this.updateRowWidth();
    },
    onColumnMove: function(container, column, header, fromIdx, toIdx) {
        this.moveColumn(column, fromIdx, toIdx);
    },
    onColumnRemove: function(container, column) {
        this.removeColumn(column);
        this.updateRowWidth();
    },
    onColumnShow: function(container, column) {
        this.showColumn(column);
        this.updateRowWidth();
    },
    onColumnHide: function(container, column) {
        this.hideColumn(column);
        this.updateRowWidth();
    },
    onColumnResize: function(container, column, width) {
        this.setColumnWidth(column, width);
        this.updateRowWidth();
    },
    updateRowWidth: function() {
        this.setWidth(this.getGrid().getTotalColumnWidth());
    },
    doUpdateSummary: function() {
        var me = this,
            grid = me.getGrid(),
            store = grid.getStore(),
            columns = grid.getColumns(),
            ln = columns.length,
            emptyText = me.getEmptyText(),
            emptyCls = me.getEmptyCls(),
            i, column, type, renderer, cell, value, field;
        for (i = 0; i < ln; i++) {
            column = columns[i];
            type = column.getSummaryType();
            cell = me.getCellByColumn(column);
            if (!column.getIgnore() && type !== null) {
                field = column.getDataIndex();
                renderer = column.getSummaryRenderer();
                if (Ext.isFunction(type)) {
                    value = type.call(store, store.data.items.slice(), field);
                } else {
                    switch (type) {
                        case 'sum':
                        case 'average':
                        case 'min':
                        case 'max':
                            value = store[type](column.getDataIndex());
                            break;
                        case 'count':
                            value = store.getCount();
                            break;
                        default:
                            value = Ext.callback(type, null, [
                                store.data.items.slice(),
                                field,
                                store
                            ], 0, me);
                            break;
                    }
                }
                if (renderer !== null) {
                    type = typeof renderer;
                    if (type === 'function') {
                        value = renderer.call(store, value, store, field, cell);
                    } else if (type === 'string') {
                        value = Ext.callback(renderer, null, [
                            value,
                            store,
                            field,
                            cell
                        ], 0, me);
                    }
                }
                cell.element.removeCls(emptyCls);
                cell.setValue(value);
            } else {
                cell.element.addCls(emptyCls);
                cell.setValue(emptyText);
            }
        }
    },
    destroy: function() {
        this.setGrid(null);
        this.callParent();
    },
    privates: {
        
        applyViewModel: function() {
            return undefined;
        },
        
        getCellCfg: function(column) {
            var cfg = Ext.apply({}, this.callParent([
                    column
                ]));
            delete cfg.bind;
            return cfg;
        }
    }
});


Ext.define('Ext.plugin.SortableList', {
    extend: Ext.Component,
    alias: 'plugin.sortablelist',
    mixins: [
        Ext.mixin.Hookable
    ],
    config: {
        list: null,
        handleSelector: '.' + Ext.baseCSSPrefix + 'list-sortablehandle'
    },
    init: function(list) {
        this.setList(list);
    },
    updateList: function(list) {
        if (list) {
            if (list.initialized) {
                this.attachListeners();
            } else {
                list.on({
                    initialize: 'attachListeners',
                    scope: this,
                    single: true
                });
            }
        }
    },
    attachListeners: function() {
        var list = this.getList(),
            scrollerElement = list.getScrollable().getElement();
        this.scrollerElement = scrollerElement;
        scrollerElement.onBefore({
            dragstart: 'onScrollerDragStart',
            scope: this
        });
    },
    onScrollerDragStart: function(e, target) {
        if (Ext.DomQuery.is(target, this.getHandleSelector())) {
            if (!this.animating) {
                this.onDragStart(e, target);
            }
            return false;
        }
    },
    onDragStart: function(e) {
        var row = Ext.getCmp(e.getTarget('.' + Ext.baseCSSPrefix + 'list-item').id),
            list = this.getList(),
            store = list.getStore();
        this.scrollerElement.on({
            drag: 'onDrag',
            dragend: 'onDragEnd',
            scope: this
        });
        this.positionMap = list.getItemMap();
        this.listStore = store;
        this.previousIndexDistance = 0;
        this.dragRow = row;
        this.dragRecord = row.getRecord();
        this.dragRowIndex = this.currentDragRowIndex = row.$dataIndex;
        this.dragRowHeight = this.positionMap.getItemHeight(this.dragRowIndex);
        if (list.getInfinite()) {
            this.startTranslate = this.positionMap.map[this.dragRowIndex];
        } else {
            row.translate(0, 0);
            this.startTranslate = 0;
        }
        row.addCls(Ext.baseCSSPrefix + 'list-item-dragging');
    },
    onDrag: function(e) {
        var list = this.getList(),
            listItems = list.listItems,
            store = list.getStore(),
            collection = list.getStore().data,
            dragRow = this.dragRow,
            dragRecordKey = dragRow.id,
            listItemInfo = list.getListItemInfo(),
            positionMap = this.positionMap,
            distance = 0,
            i, item, ln, targetItem, targetIndex, itemIndex, swapIndex, swapPosition, record, swapKey, draggingUp;
        this.dragRowPosition = this.startTranslate + e.deltaY;
        dragRow.translate(0, this.dragRowPosition);
        targetIndex = positionMap.findIndex(this.dragRowPosition + (this.dragRowHeight / 2));
        targetItem = list.getItemAt(targetIndex);
        if (targetItem) {
            distance = targetIndex - this.currentDragRowIndex;
            if (distance !== 0) {
                draggingUp = distance < 0;
                for (i = 0 , ln = Math.abs(distance); i < ln; i++) {
                    if (draggingUp) {
                        swapIndex = this.currentDragRowIndex - i;
                        item = list.getItemAt(swapIndex - 1);
                    } else {
                        swapIndex = this.currentDragRowIndex + i;
                        item = list.getItemAt(swapIndex + 1);
                    }
                    swapPosition = positionMap.map[swapIndex];
                    item.translate(0, swapPosition);
                    record = item.getRecord();
                    swapKey = record.id;
                    Ext.Array.remove(collection.items, record);
                    collection.items.splice(swapIndex, 0, record);
                    collection.indices[dragRecordKey] = collection.indices[swapKey];
                    collection.indices[swapKey] = swapIndex;
                    list.updateListItem(item, swapIndex, listItemInfo);
                    item.$position = swapPosition;
                }
                itemIndex = listItems.indexOf(dragRow);
                Ext.Array.remove(listItems, dragRow);
                listItems.splice(itemIndex + distance, 0, dragRow);
                dragRow.$dataIndex = targetIndex;
                dragRow.$position = positionMap.map[targetIndex];
                this.currentDragRowIndex = targetIndex;
            }
        }
    },
    onDragEnd: function() {
        var me = this,
            row = me.dragRow,
            list = me.getList(),
            listItemInfo = list.getListItemInfo(),
            position = row.$position;
        me.scrollerElement.un({
            drag: 'onDrag',
            dragend: 'onDragEnd',
            scope: me
        });
        me.animating = true;
        row.getTranslatable().on('animationend', function() {
            row.removeCls(Ext.baseCSSPrefix + 'list-item-dragging');
            var currentIdx = this.currentDragRowIndex,
                dragIdx = this.dragRowIndex;
            if (currentIdx !== dragIdx) {
                list.updateListItem(row, row.$dataIndex, listItemInfo);
                row.$position = position;
                list.fireEvent('dragsort', list, row, currentIdx, dragIdx);
            }
            me.animating = false;
        }, me, {
            single: true
        });
        row.translate(0, position, {
            duration: 100
        });
    }
});


Ext.define('Ext.grid.plugin.ViewOptions', {
    extend: Ext.Component,
    alias: 'plugin.gridviewoptions',
    config: {
        
        grid: null,
        
        sheetWidth: 320,
        
        sheet: {
            baseCls: Ext.baseCSSPrefix + 'grid-viewoptions',
            xtype: 'sheet',
            items: [
                {
                    docked: 'top',
                    xtype: 'titlebar',
                    title: 'Customize',
                    items: {
                        xtype: 'button',
                        text: 'Done',
                        ui: 'action',
                        align: 'right',
                        role: 'donebutton'
                    }
                }
            ],
            hideOnMaskTap: false,
            enter: 'right',
            exit: 'right',
            modal: false,
            translatable: {
                translationMethod: 'csstransform'
            },
            right: 0,
            layout: 'fit',
            stretchY: true
        },
        
        columnList: {
            xtype: 'nestedlist',
            title: 'Column',
            listConfig: {
                plugins: [
                    {
                        type: 'sortablelist',
                        handleSelector: '.' + Ext.baseCSSPrefix + 'column-options-sortablehandle'
                    }
                ],
                mode: 'MULTI',
                infinite: true,
                itemTpl: [
                    '<div class="' + Ext.baseCSSPrefix + 'column-options-itemwrap<tpl if="hidden"> {hiddenCls}</tpl>',
                    '<tpl if="grouped"> {groupedCls}</tpl>">',
                    '<div class="' + Ext.baseCSSPrefix + 'column-options-sortablehandle"></div>',
                    '<tpl if="header">',
                    '<div class="' + Ext.baseCSSPrefix + 'column-options-folder"></div>',
                    '<tpl else>',
                    '<div class="' + Ext.baseCSSPrefix + 'column-options-leaf"></div>',
                    '</tpl>',
                    '<div class="' + Ext.baseCSSPrefix + 'column-options-text">{text}</div>',
                    '<tpl if="groupable && dataIndex">',
                    '<div class="' + Ext.baseCSSPrefix + 'column-options-groupindicator"></div>',
                    '</tpl>',
                    '<div class="' + Ext.baseCSSPrefix + 'column-options-visibleindicator"></div>',
                    '</div>'
                ],
                triggerEvent: null,
                bufferSize: 1,
                minimumBufferSize: 1
            },
            store: {
                type: 'tree',
                fields: [
                    'id',
                    'text',
                    'dataIndex',
                    'header',
                    'hidden',
                    'hiddenCls',
                    'grouped',
                    'groupedCls',
                    'groupable'
                ],
                root: {
                    text: 'Columns'
                }
            },
            clearSelectionOnListChange: false
        },
        
        visibleIndicatorSelector: '.' + Ext.baseCSSPrefix + 'column-options-visibleindicator',
        
        groupIndicatorSelector: '.' + Ext.baseCSSPrefix + 'column-options-groupindicator'
    },
    
    _hiddenColumnCls: Ext.baseCSSPrefix + 'column-options-hidden',
    
    _groupedColumnCls: Ext.baseCSSPrefix + 'column-options-grouped',
    
    sheetVisible: false,
    init: function(grid) {
        this.setGrid(grid);
    },
    updateGrid: function(grid, oldGrid) {
        if (oldGrid) {
            oldGrid.getHeaderContainer().renderElement.un({
                dragstart: 'onDragStart',
                drag: 'onDrag',
                dragend: 'onDragEnd',
                longpress: 'onHeaderLongPress',
                scope: this
            });
            oldGrid.getHeaderContainer().un({
                columnadd: 'onColumnAdd',
                columnmove: 'onColumnMove',
                columnremove: 'onColumnRemove',
                scope: this
            });
        }
        if (grid) {
            grid.getHeaderContainer().renderElement.on({
                dragstart: 'onDragStart',
                drag: 'onDrag',
                dragend: 'onDragEnd',
                longpress: 'onHeaderLongPress',
                scope: this
            });
            grid.getHeaderContainer().on({
                columnadd: 'onColumnAdd',
                columnmove: 'onColumnMove',
                columnremove: 'onColumnRemove',
                columnhide: 'onColumnHide',
                columnshow: 'onColumnShow',
                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: 'onColumnReorder',
                delegate: '> list',
                scope: this
            });
            this.attachTapListeners();
        }
    },
    updateSheet: function(sheet) {
        var sheetWidth = this.getSheetWidth();
        sheet.setWidth(sheetWidth);
        sheet.translate(sheetWidth);
        sheet.add(this.getColumnList());
    },
    onDoneButtonTap: function() {
        this.hideViewOptions();
    },
    onColumnReorder: 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({
                itemtap: 'onListItemTap',
                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();
    },
    onListItemTap: function(list, index, row, record, e) {
        var me = this,
            handled = false;
        if (Ext.fly(e.target).is(me.getVisibleIndicatorSelector())) {
            me.onVisibleIndicatorTap(row, record, index);
            handled = true;
        } else if (Ext.fly(e.target).is(me.getGroupIndicatorSelector())) {
            me.onGroupIndicatorTap(row, record, index);
            handled = true;
        }
        return !handled;
    },
    onVisibleIndicatorTap: function(row, record) {
        var hidden = !record.get('hidden'),
            column = Ext.getCmp(record.get('id'));
        if (hidden) {
            column.hide();
            record.set('hidden', true);
        } else {
            column.show();
            record.set('hidden', false);
        }
    },
    onGroupIndicatorTap: function(row, record) {
        var me = this,
            grouped = !record.get('grouped'),
            store = me.getGrid().getStore(),
            groupedRecord = me._groupedRecord;
        if (groupedRecord) {
            groupedRecord.set('grouped', false);
        }
        if (grouped) {
            store.setGrouper({
                property: record.get('dataIndex')
            });
            me._groupedRecord = record;
            record.set('grouped', true);
        } else {
            store.setGrouper(null);
            me._groupedRecord = null;
            record.set('grouped', false);
        }
    },
    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(headerContainer, column, header) {
        if (column.getIgnore() || this.isMoving) {
            return;
        }
        var me = this,
            nestedList = me.getColumnList(),
            store = nestedList.getStore(),
            parentNode = store.getRoot(),
            hiddenCls = me._hiddenColumnCls,
            grid = me.getGrid(),
            isGridGrouped = grid.getGrouped(),
            grouper = grid.getStore().getGrouper(),
            dataIndex = column.getDataIndex(),
            data = {
                id: column.getId(),
                text: column.getText(),
                groupable: isGridGrouped && column.getGroupable(),
                hidden: column.isHidden(),
                hiddenCls: hiddenCls,
                grouped: !!(isGridGrouped && grouper && grouper.getProperty() === dataIndex),
                groupedCls: me._groupedColumnCls,
                dataIndex: column.getDataIndex(),
                leaf: true
            },
            idx, headerNode;
        if (header) {
            headerNode = parentNode.findChild('id', header.getId());
            if (!headerNode) {
                idx = header.getParent().indexOf(header);
                headerNode = parentNode.insertChild(idx, {
                    groupable: false,
                    header: true,
                    hidden: header.isHidden(),
                    hiddenCls: hiddenCls,
                    id: header.getId(),
                    text: header.getText()
                });
            }
            idx = header.indexOf(column);
            parentNode = headerNode;
        } else {
            idx = headerContainer.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.getColumnList().getStore().getRoot(),
            record = root.findChild('id', column.getId(), true);
        if (record) {
            record.parentNode.removeChild(record, true);
        }
    },
    onDragStart: function() {
        var sheetWidth = this.getSheetWidth(),
            sheet = this.getSheet();
        if (!this.sheetVisible) {
            sheet.translate(sheetWidth);
            this.startTranslate = sheetWidth;
        } else {
            sheet.translate(0);
            this.startTranslate = 0;
        }
    },
    onDrag: function(e) {
        this.getSheet().translate(Math.max(this.startTranslate + e.deltaX, 0));
    },
    onDragEnd: function(e) {
        var me = this;
        if (e.flick.velocity.x > 0.1) {
            me.hideViewOptions();
        } else {
            me.showViewOptions();
        }
    },
    onHeaderLongPress: function(e) {
        if (!this.sheetVisible) {
            this.showViewOptions();
        }
    },
    hideViewOptions: function() {
        var sheet = this.getSheet();
        this.getGrid().getHeaderContainer().setSortable(true);
        sheet.translate(this.getSheetWidth(), 0, {
            duration: 100
        });
        sheet.getTranslatable().on('animationend', function() {
            if (sheet.getModal()) {
                sheet.getModal().destroy();
                sheet.setModal(null);
            }
            sheet.hide(null);
        }, this, {
            single: true
        });
        this.sheetVisible = false;
    },
    showViewOptions: function() {
        var me = this,
            sheet = me.getSheet(),
            modal = null;
        me.setup();
        if (!me.sheetVisible) {
            
            
            
            me.getGrid().getHeaderContainer().setSortable(false);
            me.updateListInfo();
            sheet.show();
            sheet.translate(0, 0, {
                duration: 100
            });
            sheet.getTranslatable().on('animationend', function() {
                sheet.setModal(true);
                modal = sheet.getModal();
                modal.element.onBefore({
                    tap: 'hideViewOptions',
                    dragstart: 'onDragStart',
                    drag: 'onDrag',
                    dragend: 'onDragEnd',
                    scope: me
                });
            }, me, {
                single: true
            });
            me.sheetVisible = true;
        }
    },
    privates: {
        setup: function() {
            var me = this,
                sheet;
            if (me.doneSetup) {
                return;
            }
            me.doneSetup = true;
            sheet = me.getSheet();
            me.getGrid().add(sheet);
            sheet.translate(me.getSheetWidth());
            sheet.down('button[role=donebutton]').on({
                tap: 'onDoneButtonTap',
                scope: me
            });
        },
        updateListInfo: function() {
            var grid = this.getGrid(),
                store = grid.getStore(),
                grouper = store.getGrouper(),
                grouperProp = grouper && grouper.getProperty(),
                headerContainer = grid.getHeaderContainer();
            this.getColumnList().getStore().getRoot().cascadeBy(function(node) {
                var dataIndex = node.get('dataIndex');
                node.set('grouped', dataIndex && dataIndex === grouperProp);
            });
        }
    }
});


Ext.define('Ext.navigation.Bar', {
    extend: Ext.TitleBar,
    
    isToolbar: true,
    config: {
        
        baseCls: Ext.baseCSSPrefix + 'toolbar',
        
        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
        }
    },
    
    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: {
        
        baseCls: Ext.baseCSSPrefix + 'navigationview',
        
        navigationBar: {
            docked: 'top'
        },
        
        defaultBackButtonText: 'Back',
        
        useTitleForBackButtonText: false,
        
        
        layout: {
            type: 'card',
            animation: {
                duration: 300,
                easing: 'ease-out',
                type: 'slide',
                direction: 'left'
            }
        }
    },
    
    
    
    
    initialize: function() {
        var me = this,
            navBar = me.getNavigationBar();
        
        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');
        }
    },
    
    
    applyLayout: function(config) {
        config = config || {};
        return config;
    },
    
    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.Header', {
    extend: Ext.Container,
    xtype: 'panelheader',
    
    isPanelHeader: true,
    baseCls: Ext.baseCSSPrefix + 'panel-header',
    config: {
        
        glyph: null,
        
        icon: null,
        
        iconAlign: null,
        
        iconCls: null,
        
        title: null,
        
        titleAlign: null,
        layout: {
            type: 'hbox',
            align: 'center'
        }
    },
    add: function(item) {
        var me = this,
            isArray = Ext.isArray(item),
            array = isArray ? item.slice(0) : [
                item
            ],
            
            items = me.getItems(),
            length = items.length,
            n = array.length,
            c, i, n, pos;
        for (i = 0; i < n; ++i) {
            
            
            array[i] = me.factoryItem(array[i]);
        }
        Ext.Array.sort(array, me.sortByWeight);
        if (length) {
            items = items.items;
            
            pos = 0;
            
            
            
            
            for (i = 0; i < n; ++i) {
                c = array[i];
                for (; pos < length; ++pos) {
                    if (me.sortByWeight(c, items[pos]) < 0) {
                        break;
                    }
                }
                me.insert(pos, c);
                ++pos;
                ++length;
            }
        } else {
            me.callParent([
                array
            ]);
        }
        return isArray ? array : item;
    },
    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 ret = {
                xtype: 'paneltitle',
                flex: 1
            };
        if (config) {
            if (typeof config === 'string') {
                config = {
                    text: config
                };
            }
            Ext.merge(ret, config);
        }
        return ret;
    },
    createTools: function(tools, toolOwner) {
        var n = tools && tools.length,
            ret = n && [],
            c, i;
        toolOwner = toolOwner || null;
        for (i = 0; i < n; ++i) {
            c = tools[i];
            if (typeof c === 'string') {
                c = {
                    xtype: 'paneltool',
                    type: c,
                    toolOwner: toolOwner
                };
            } else if (c.isInstance) {
                if (toolOwner) {
                    c.setToolOwner(toolOwner);
                }
            } else {
                c = Ext.apply({
                    xtype: 'paneltool',
                    toolOwner: toolOwner
                }, c);
            }
            ret[i] = c;
        }
        return ret;
    },
    updateGlyph: function(glyph) {
        this.ensureTitle().setGlyph(glyph);
    },
    updateIcon: function(icon) {
        this.ensureTitle().setIcon(icon);
    },
    updateIconAlign: function(align, oldAlign) {
        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, oldAlign) {
        this.ensureTitle().setTextAlign(align);
    },
    updateUi: function(ui, oldValue) {
        this.callParent([
            ui,
            oldValue
        ]);
        this.ensureTitle().setUi(ui);
    },
    privates: {
        clearTools: function() {
            var items = this.getItems().items,
                c, i;
            for (i = items.length; i-- > 0; ) {
                c = items[i];
                if (c.isPanelTool) {
                    this.remove(c);
                }
            }
        },
        ensureTitle: function() {
            var me = this,
                title = me.getTitle();
            if (!title) {
                me.setTitle('');
                title = me.getTitle();
            }
            return title;
        },
        sortByWeight: function(item1, item2) {
            return (item1.weight || 0) - (item2.weight || 0);
        }
    }
});


Ext.define('Ext.panel.Title', {
    extend: Ext.Component,
    xtype: 'paneltitle',
    isPanelTitle: true,
    
    
    
    _textAlign: 'left',
    _iconAlign: 'left',
    _text: '&#160;',
    cachedConfig: {
        
        textAlign: null,
        
        text: null,
        
        glyph: null,
        
        icon: null,
        
        iconAlign: null,
        
        iconCls: null
    },
    weight: -10,
    element: {
        unselectable: 'on',
        reference: 'element',
        cls: Ext.baseCSSPrefix + 'panel-title-align-left',
        children: [
            {
                reference: 'iconElement',
                style: 'display:none',
                cls: Ext.baseCSSPrefix + 'panel-title-icon ' + Ext.baseCSSPrefix + 'panel-title-icon-left'
            },
            {
                reference: 'textElement',
                cls: Ext.baseCSSPrefix + 'panel-title-text'
            }
        ]
    },
    _textAlignClasses: {
        left: Ext.baseCSSPrefix + 'panel-title-align-left',
        center: Ext.baseCSSPrefix + 'panel-title-align-center',
        right: Ext.baseCSSPrefix + 'panel-title-align-right'
    },
    _iconAlignClasses: {
        top: Ext.baseCSSPrefix + 'panel-title-icon-top',
        right: Ext.baseCSSPrefix + 'panel-title-icon-right',
        bottom: Ext.baseCSSPrefix + 'panel-title-icon-bottom',
        left: Ext.baseCSSPrefix + 'panel-title-icon-left'
    },
    baseCls: Ext.baseCSSPrefix + 'panel-title',
    _titleSuffix: '-title',
    _glyphCls: Ext.baseCSSPrefix + 'panel-title-glyph',
    _verticalCls: Ext.baseCSSPrefix + 'panel-title-vertical',
    applyText: function(text) {
        return text || '&#160;';
    },
    updateGlyph: function(glyph, oldGlyph) {
        glyph = glyph || 0;
        var me = this,
            glyphCls = me._glyphCls,
            iconEl = me.iconElement,
            fontFamily, glyphParts;
        me.glyph = glyph;
        me._syncIconVisibility();
        if (typeof glyph === 'string') {
            glyphParts = glyph.split('@');
            glyph = glyphParts[0];
            fontFamily = glyphParts[1] || Ext._glyphFontFamily;
        }
        if (!glyph) {
            iconEl.dom.innerHTML = '';
            iconEl.removeCls(glyphCls);
        } else {
            iconEl.dom.innerHTML = '&#' + glyph + ';';
            iconEl.addCls(glyphCls);
        }
        if (fontFamily) {
            iconEl.setStyle('font-family', fontFamily);
        }
    },
    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,
            iconEl = me.iconElement,
            iconAlignClasses = me._iconAlignClasses,
            el = me.el;
        if (oldAlign) {
            iconEl.removeCls(iconAlignClasses[oldAlign]);
        }
        iconEl.addCls(iconAlignClasses[align]);
        
        
        if (align === 'top' || align === 'left') {
            el.insertFirst(iconEl);
        } else {
            el.appendChild(iconEl);
        }
        if (align === 'top' || align === 'bottom') {
            el.addCls(me._verticalCls);
        } else {
            el.removeCls(me._verticalCls);
        }
    },
    updateIconCls: function(cls, oldCls) {
        var iconEl = this.iconElement;
        this._syncIconVisibility();
        if (oldCls) {
            iconEl.removeCls(oldCls);
        }
        if (cls) {
            iconEl.addCls(cls);
        }
    },
    updateText: function(text) {
        this.textElement.setHtml(text);
    },
    updateTextAlign: function(align, oldAlign) {
        var me = this,
            textAlignClasses = me._textAlignClasses;
        if (oldAlign) {
            me.removeCls(textAlignClasses[oldAlign]);
        }
        me.addCls(textAlignClasses[align]);
    },
    privates: {
        _getVerticalAdjustDirection: function() {
            
            return 'left';
        },
        _hasIcon: function() {
            return !!(this.getIcon() || this.getIconCls() || this.getGlyph());
        },
        _syncIconVisibility: function() {
            this.iconElement.setDisplayed(this._hasIcon());
        }
    }
});


Ext.define('Ext.panel.Tool', {
    extend: Ext.Component,
    xtype: [
        'paneltool',
        'tool'
    ],
    
    
    isPanelTool: true,
    baseCls: Ext.baseCSSPrefix + 'tool',
    disabledCls: Ext.baseCSSPrefix + 'tool-disabled',
    toolPressedCls: Ext.baseCSSPrefix + 'tool-pressed',
    toolOverCls: Ext.baseCSSPrefix + 'tool-over',
    element: {
        reference: 'element',
        listeners: {
            click: 'onClick',
            mousedown: 'onMouseDown',
            mouseover: 'onMouseOver',
            mouseout: 'onMouseOut'
        },
        children: [
            {
                reference: 'toolElement'
            }
        ]
    },
    
    handler: null,
    
    scope: null,
    
    toolOwner: null,
    config: {
        
        iconCls: null,
        
        type: null
    },
    
    stopEvent: true,
    
    
    
    
    
    
    
    
    weight: 10,
    updateIconCls: function(iconCls, oldValue) {
        var toolEl = this.toolElement;
        toolEl.replaceCls(oldValue, iconCls);
    },
    updateType: function(type, oldType) {
        var baseCls = this.getBaseCls(),
            toolEl = this.toolElement;
        toolEl.replaceCls(oldType && (baseCls + '-' + oldType), type && (baseCls + '-' + type));
    },
    privates: {
        
        _toolTypes: {
            close: 1,
            collapse: 1,
            down: 1,
            expand: 1,
            gear: 1,
            help: 1,
            left: 1,
            maximize: 1,
            minimize: 1,
            minus: 1,
            
            next: 1,
            pin: 1,
            plus: 1,
            prev: 1,
            print: 1,
            refresh: 1,
            
            restore: 1,
            right: 1,
            save: 1,
            search: 1,
            toggle: 1,
            unpin: 1,
            up: 1
        },
        
        
        onClick: function(e, target) {
            var me = this,
                handler = me.handler || me.callback;
            
            if (me.disabled) {
                return false;
            }
            
            if (e.type !== 'keydown') {
                me.el.removeCls(me.toolPressedCls + ' ' + me.toolOverCls);
            }
            if (me.stopEvent !== false) {
                e.stopEvent();
            }
            if (handler) {
                Ext.callback(handler, me.scope, [
                    me.toolOwner || me.parent,
                    me,
                    e
                ], 0, me);
            }
            
            me.fireEvent('click', me, e, me.toolOwner || me.ownerCt);
            return true;
        },
        
        onMouseDown: function(e) {
            
            
            
            e.preventDefault();
            if (this.disabled) {
                return false;
            }
            this.el.addCls(this.toolPressedCls);
        },
        
        onMouseOver: function() {
            if (this.disabled) {
                return false;
            }
            this.el.addCls(this.toolOverCls);
        },
        
        onMouseOut: function() {
            this.el.removeCls(this.toolOverCls);
        }
    }
});


Ext.define('Ext.plugin.ListPaging', {
    extend: Ext.Component,
    alias: 'plugin.listpaging',
    config: {
        
        autoPaging: false,
        
        loadMoreText: 'Load More...',
        
        noMoreRecordsText: 'No More Records',
        
        loadTpl: [
            '<div class="{cssPrefix}loading-spinner" style="font-size: 180%; margin: 10px auto;">',
            '<span class="{cssPrefix}loading-top"></span>',
            '<span class="{cssPrefix}loading-right"></span>',
            '<span class="{cssPrefix}loading-bottom"></span>',
            '<span class="{cssPrefix}loading-left"></span>',
            '</div>',
            '<div class="{cssPrefix}list-paging-msg">{message}</div>'
        ].join(''),
        
        loadMoreCmp: {
            xtype: 'component',
            baseCls: Ext.baseCSSPrefix + 'list-paging',
            scrollDock: 'bottom',
            hidden: true
        },
        
        loadMoreCmpAdded: false,
        
        loadingCls: Ext.baseCSSPrefix + 'loading',
        
        list: null,
        
        scroller: null,
        
        loading: false
    },
    
    init: function(list) {
        var scroller = list.getScrollable(),
            store = list.getStore();
        this.setList(list);
        this.setScroller(scroller);
        this.bindStore(list.getStore());
        this.addLoadMoreCmp();
        
        list.updateStore = Ext.Function.createInterceptor(list.updateStore, this.bindStore, this);
        if (this.getAutoPaging()) {
            scroller.on({
                scrollend: this.onScrollEnd,
                scope: this
            });
        }
    },
    
    bindStore: function(newStore, oldStore) {
        if (oldStore) {
            oldStore.un({
                beforeload: this.onStoreBeforeLoad,
                load: this.onStoreLoad,
                filter: this.onFilter,
                scope: this
            });
        }
        if (newStore) {
            newStore.on({
                beforeload: this.onStoreBeforeLoad,
                load: this.onStoreLoad,
                filter: this.onFilter,
                scope: this
            });
        }
    },
    
    disableDataViewMask: function() {
        var list = this.getList();
        this._listMask = list.getLoadingText();
        list.setLoadingText(null);
    },
    enableDataViewMask: function() {
        if (this._listMask) {
            var list = this.getList();
            list.setLoadingText(this._listMask);
            delete this._listMask;
        }
    },
    
    applyLoadTpl: function(config) {
        return (Ext.isObject(config) && config.isTemplate) ? config : new Ext.XTemplate(config);
    },
    
    applyLoadMoreCmp: function(config) {
        config = Ext.merge(config, {
            html: this.getLoadTpl().apply({
                cssPrefix: Ext.baseCSSPrefix,
                message: this.getLoadMoreText()
            }),
            scrollDock: 'bottom',
            listeners: {
                tap: {
                    fn: this.loadNextPage,
                    scope: this,
                    element: 'element'
                }
            }
        });
        return Ext.factory(config, Ext.Component, this.getLoadMoreCmp());
    },
    
    onScrollEnd: function(scroller, x, y) {
        var list = this.getList();
        if (!this.getLoading() && y >= scroller.getMaxUserPosition().y) {
            this.currentScrollToTopOnRefresh = list.getScrollToTopOnRefresh();
            list.setScrollToTopOnRefresh(false);
            this.loadNextPage();
        }
    },
    
    updateLoading: function(isLoading) {
        var loadMoreCmp = this.getLoadMoreCmp(),
            loadMoreCls = this.getLoadingCls();
        if (isLoading) {
            loadMoreCmp.addCls(loadMoreCls);
        } else {
            loadMoreCmp.removeCls(loadMoreCls);
        }
    },
    
    onStoreBeforeLoad: function(store) {
        if (store.getCount() === 0) {
            this.getLoadMoreCmp().hide();
        }
    },
    
    onStoreLoad: function(store) {
        var loadCmp = this.getLoadMoreCmp(),
            template = this.getLoadTpl(),
            message = this.storeFullyLoaded() ? this.getNoMoreRecordsText() : this.getLoadMoreText();
        if (store.getCount()) {
            loadCmp.show();
        }
        this.setLoading(false);
        
        loadCmp.setHtml(template.apply({
            cssPrefix: Ext.baseCSSPrefix,
            message: message
        }));
        if (this.currentScrollToTopOnRefresh !== undefined) {
            this.getList().setScrollToTopOnRefresh(this.currentScrollToTopOnRefresh);
            delete this.currentScrollToTopOnRefresh;
        }
        this.enableDataViewMask();
    },
    onFilter: function(store) {
        if (store.getCount() === 0) {
            this.getLoadMoreCmp().hide();
        } else {
            this.getLoadMoreCmp().show();
        }
    },
    
    addLoadMoreCmp: function() {
        var list = this.getList(),
            cmp = this.getLoadMoreCmp();
        if (!this.getLoadMoreCmpAdded()) {
            list.add(cmp);
            
            list.fireEvent('loadmorecmpadded', this, list);
            this.setLoadMoreCmpAdded(true);
        }
        return cmp;
    },
    
    storeFullyLoaded: function() {
        var store = this.getList().getStore(),
            total = store.getTotalCount();
        return total !== null ? store.getTotalCount() <= (store.currentPage * store.getPageSize()) : false;
    },
    
    loadNextPage: function() {
        var me = this;
        if (!me.storeFullyLoaded()) {
            me.disableDataViewMask();
            me.setLoading(true);
            me.getList().getStore().nextPage({
                addRecords: true
            });
        }
    }
});


Ext.define('Ext.plugin.PullRefresh', {
    extend: Ext.Component,
    alias: 'plugin.pullrefresh',
    config: {
        width: '100%',
        
        list: null,
        
        pullText: 'Pull down to refresh...',
        
        releaseText: 'Release to refresh...',
        
        loadingText: 'Loading...',
        
        loadedText: 'Loaded.',
        
        lastUpdatedText: 'Last Updated:&nbsp;',
        
        scrollerAutoRefresh: false,
        
        autoSnapBack: true,
        
        snappingAnimationDuration: 300,
        
        lastUpdatedDateFormat: 'm/d/Y h:iA',
        
        overpullSnapBackDuration: 300,
        
        pullTpl: [
            '<div class="' + Ext.baseCSSPrefix + 'list-pullrefresh-arrow"></div>',
            '<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 + 'list-pullrefresh-wrap">',
            '<h3 class="' + Ext.baseCSSPrefix + 'list-pullrefresh-message">{message}</h3>',
            '<div class="' + Ext.baseCSSPrefix + 'list-pullrefresh-updated">{updated}</div>',
            '</div>'
        ].join(''),
        translatable: true
    },
    
    $state: 'pull',
    refreshCls: Ext.baseCSSPrefix + 'list-pullrefresh',
    
    getState: function() {
        return this.$state;
    },
    
    setState: function(value) {
        this.$state = value;
        this.updateView();
    },
    
    $isSnappingBack: false,
    
    getIsSnappingBack: function() {
        return this.$isSnappingBack;
    },
    
    setIsSnappingBack: function(value) {
        this.$isSnappingBack = value;
    },
    
    init: function(list) {
        this.setList(list);
        this.initScrollable();
    },
    getElementConfig: function() {
        return {
            reference: 'element',
            classList: [
                'x-unsized'
            ],
            children: [
                {
                    reference: 'innerElement',
                    className: this.refreshCls
                }
            ]
        };
    },
    
    initScrollable: function() {
        var me = this,
            list = me.getList(),
            scroller = list.getScrollable();
        if (!scroller || !scroller.isTouchScroller) {
            return;
        }
        scroller.setAutoRefresh(me.getScrollerAutoRefresh());
        me.lastUpdated = new Date();
        list.insert(0, me);
        scroller.on({
            scroll: me.onScrollChange,
            scope: me
        });
        me.updateView();
    },
    
    applyPullTpl: function(config) {
        if (config instanceof Ext.XTemplate) {
            return config;
        } else {
            return new Ext.XTemplate(config);
        }
    },
    
    updateList: function(newList, oldList) {
        var me = this;
        if (newList) {
            newList.on({
                order: 'after',
                scrollablechange: me.initScrollable,
                scope: me
            });
        }
        if (oldList) {
            oldList.un({
                order: 'after',
                scrollablechange: me.initScrollable,
                scope: me
            });
        }
    },
    
    getPullHeight: function() {
        return this.innerElement.getHeight();
    },
    
    fetchLatest: function() {
        this.getList().getStore().fetch({
            page: 1,
            start: 0,
            callback: this.onLatestFetched,
            scope: this
        });
    },
    
    onLatestFetched: function(newRecords) {
        var me = this,
            store = me.getList().getStore(),
            oldRecords = store.getData(),
            length = newRecords.length,
            toInsert = [],
            newRecord, oldRecord, i;
        for (i = 0; i < length; i++) {
            newRecord = newRecords[i];
            oldRecord = oldRecords.getByKey(newRecord.getId());
            if (oldRecord) {
                oldRecord.set(newRecord.getData());
            } else {
                toInsert.push(newRecord);
            }
            oldRecord = undefined;
        }
        store.insert(0, toInsert);
        me.setState('loaded');
        me.fireEvent('latestfetched', me, toInsert);
        if (me.getAutoSnapBack()) {
            me.snapBack();
        }
    },
    
    snapBack: function(force) {
        var me = this,
            list, scroller;
        if (this.getState() !== 'loaded' && force !== true) {
            return;
        }
        list = me.getList();
        scroller = list.getScrollable();
        me.setIsSnappingBack(true);
        scroller.doScrollTo(null, 0, {
            callback: Ext.bind(me.onSnapBackEnd, me),
            duration: me.getSnappingAnimationDuration()
        });
    },
    
    onSnapBackEnd: function() {
        var list = this.getList(),
            scroller = list.getScrollable();
        scroller.setMinUserPosition({
            x: 0,
            y: 0
        });
        this.setState('pull');
        this.setIsSnappingBack(false);
    },
    
    onScrollChange: function(scroller, x, y) {
        if (y > 0) {
            return;
        }
        var me = this,
            pullHeight = me.getPullHeight(),
            isSnappingBack = me.getIsSnappingBack(),
            state = me.getState();
        if (state === 'loaded' && !isSnappingBack) {
            me.snapBack();
        }
        if (state !== 'loading' && state !== 'loaded') {
            if (-y >= pullHeight + 10) {
                me.setState('release');
                scroller.getElement().onBefore({
                    dragend: 'onScrollerDragEnd',
                    single: true,
                    scope: me
                });
            } else if (state === "release" && (-y < pullHeight + 10)) {
                me.setState('pull');
                scroller.getElement().unBefore({
                    dragend: 'onScrollerDragEnd',
                    single: true,
                    scope: me
                });
            }
        }
        me.getTranslatable().translate(0, -y);
    },
    
    onScrollerDragEnd: function() {
        var me = this,
            pullHeight, list, scroller;
        if (me.getState() === 'loading') {
            return;
        }
        list = me.getList();
        scroller = list.getScrollable();
        pullHeight = me.getPullHeight();
        me.setState('loading');
        scroller.setMinUserPosition({
            x: 0,
            y: -pullHeight
        });
        scroller.doScrollTo(0, -pullHeight, {
            callback: Ext.bind(me.fetchLatest, me),
            easingY: {
                duration: me.getOverpullSnapBackDuration()
            }
        }, true);
    },
    
    updateView: function() {
        var me = this,
            innerElement = me.innerElement,
            state = me.getState(),
            lastUpdatedText = me.getLastUpdatedText() + Ext.util.Format.date(me.lastUpdated, me.getLastUpdatedDateFormat()),
            templateConfig = {
                state: state,
                updated: lastUpdatedText
            },
            stateFn = state.charAt(0).toUpperCase() + state.slice(1).toLowerCase(),
            fn = 'get' + stateFn + 'Text';
        if (me[fn] && Ext.isFunction(me[fn])) {
            templateConfig.message = me[fn].call(me);
        }
        innerElement.removeCls([
            'loaded',
            'loading',
            'release',
            'pull'
        ], me.refreshCls);
        innerElement.addCls(state, me.refreshCls);
        me.getPullTpl().overwrite(innerElement, templateConfig);
    }
});


Ext.define('Ext.plugin.Responsive', {
    extend: Ext.mixin.Responsive,
    alias: 'plugin.responsive',
    pluginId: 'responsive',
    isPlugin: true,
    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 = Ext.apply({
                responsiveConfig: cmpConfig.responsiveConfig,
                responsiveFormulas: cmpConfig.responsiveFormulas
            }, config),
            transformed;
        delete c.cmp;
        delete c.type;
        me.cmp = cmp;
        me.initConfig(c);
        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.field.PlaceHolderLabel', {
    extend: Ext.AbstractPlugin,
    alias: 'plugin.placeholderlabel',
    config: {
        
        field: null,
        
        labelVisible: false,
        
        cls: Ext.baseCSSPrefix + 'placeholderlabel',
        
        showCls: Ext.baseCSSPrefix + 'show-label'
    },
    constructor: function(config) {
        this.initConfig(config);
        this.callParent([
            config
        ]);
    },
    init: function(field) {
        this.setField(field);
        
        if (field.getValue()) {
            this.setLabelVisible(true);
        }
    },
    
    maybeShowLabel: function(field) {
        this.setLabelVisible(!!field.getValue());
    },
    
    getFieldListeners: function() {
        return {
            scope: this,
            keyup: this.maybeShowLabel,
            clearicontap: this.maybeShowLabel
        };
    },
    updateField: function(newField, oldField) {
        var listeners = this.getFieldListeners(),
            cls = this.getCls(),
            label, config;
        if (oldField) {
            
            oldField.un(listeners);
            
            oldField.removeCls(cls);
        }
        if (newField) {
            label = newField.getLabel();
            config = {
                labelAlign: 'top'
            };
            
            if (!label) {
                config.label = newField.getPlaceHolder();
            }
            
            newField.on(listeners);
            
            newField.addCls(cls);
            
            newField.setConfig(config);
        }
    },
    updateLabelVisible: function(show) {
        var field = this.getField();
        if (field) {
            field.toggleCls(this.getShowCls(), show);
        }
    }
});


Ext.define('Ext.tab.Tab', {
    extend: Ext.Button,
    xtype: 'tab',
    alternateClassName: 'Ext.Tab',
    
    isTab: true,
    config: {
        
        baseCls: Ext.baseCSSPrefix + 'tab',
        
        pressedCls: Ext.baseCSSPrefix + 'tab-pressed',
        
        activeCls: Ext.baseCSSPrefix + 'tab-active',
        
        active: false,
        
        title: '&nbsp;'
    },
    updateIconCls: function(newCls, oldCls) {
        this.callParent([
            newCls,
            oldCls
        ]);
        if (oldCls) {
            this.removeCls('x-tab-icon');
        }
        if (newCls) {
            this.addCls('x-tab-icon');
        }
    },
    
    
    updateTitle: function(title) {
        this.setText(title);
    },
    updateActive: function(active, oldActive) {
        var activeCls = this.getActiveCls();
        if (active && !oldActive) {
            this.element.addCls(activeCls);
            this.fireEvent('activate', this);
        } else if (oldActive) {
            this.element.removeCls(activeCls);
            this.fireEvent('deactivate', this);
        }
    }
}, 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',
    config: {
        
        baseCls: Ext.baseCSSPrefix + 'tabbar',
        
        defaultType: 'tab',
        
        defaultTabUI: null,
        
        layout: {
            type: 'hbox',
            align: 'middle'
        }
    },
    eventedConfig: {
        
        activeTab: null
    },
    
    initialize: function() {
        var me = this;
        me.callParent();
        me.on({
            tap: 'onTabTap',
            delegate: '> tab',
            scope: me
        });
    },
    
    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) {
        if (newTab) {
            newTab.setActive(true);
        }
        
        if (oldTab && oldTab.parent) {
            oldTab.setActive(false);
        }
    },
    
    parseActiveTab: function(tab) {
        
        if (typeof tab == 'number') {
            return this.getItems().items[tab];
        } else if (typeof tab == 'string') {
            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
        ]);
    }
});


Ext.define('Ext.tab.Panel', {
    extend: Ext.Container,
    xtype: 'tabpanel',
    alternateClassName: 'Ext.TabPanel',
    config: {
        
        tabBar: true,
        
        tabBarPosition: 'top',
        
        layout: {
            type: 'card',
            animation: {
                type: 'slide',
                direction: 'left'
            }
        },
        
        cls: Ext.baseCSSPrefix + 'tabpanel'
    },
    
    
    initialize: function() {
        this.callParent();
        this.on({
            beforeactivetabchange: 'doTabChange',
            delegate: '> tabbar',
            scope: this
        });
        this.on({
            disabledchange: 'onItemDisabledChange',
            delegate: '> component',
            scope: this
        });
    },
    
    applyScrollable: function() {
        return false;
    },
    
    updateUi: function(newUi, oldUi) {
        this.callParent(arguments);
        if (this.initialized) {
            this.getTabBar().setUi(newUi);
        }
    },
    
    updateActiveItem: function(newActiveItem, oldActiveItem) {
        if (newActiveItem) {
            var items = this.getInnerItems(),
                oldIndex = items.indexOf(oldActiveItem),
                newIndex = items.indexOf(newActiveItem),
                reverse = oldIndex > newIndex,
                animation = this.getLayout().getAnimation(),
                tabBar = this.getTabBar(),
                oldTab = tabBar.parseActiveTab(oldIndex),
                newTab = tabBar.parseActiveTab(newIndex);
            if (animation && animation.setReverse) {
                animation.setReverse(reverse);
            }
            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) {
        if (config === true) {
            config = {};
        }
        if (config) {
            Ext.applyIf(config, {
                ui: this.getUi(),
                docked: this.getTabBarPosition()
            });
        }
        return Ext.factory(config, Ext.tab.Bar, this.getTabBar());
    },
    
    updateTabBar: function(newTabBar) {
        if (newTabBar) {
            this.add(newTabBar);
            this.setTabBarPosition(newTabBar.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,
            tabIconCls = (card.getIconCls) ? card.getIconCls() : initialConfig.iconCls,
            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.isPanel && card.getHeader(),
            tabInstance;
        if (tabTitle && !tabConfig.title) {
            tabConfig.title = tabTitle;
        }
        if (tabIconCls && !tabConfig.iconCls) {
            tabConfig.iconCls = tabIconCls;
        }
        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;
        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) {
        this.getTabBar().remove(item.tab, this.getAutoDestroy());
        this.callParent(arguments);
    }
});

Ext.define('Ext.table.Cell', {
    extend: Ext.Container,
    xtype: 'tablecell',
    config: {
        baseCls: 'x-table-cell'
    },
    getElementConfig: function() {
        var config = this.callParent();
        config.children.length = 0;
        return config;
    }
});

Ext.define('Ext.table.Row', {
    extend: Ext.table.Cell,
    xtype: 'tablerow',
    config: {
        baseCls: 'x-table-row',
        defaultType: 'tablecell'
    }
});

Ext.define('Ext.table.Table', {
    extend: Ext.Container,
    xtype: 'table',
    config: {
        baseCls: 'x-table',
        defaultType: 'tablerow'
    },
    cachedConfig: {
        fixedLayout: false
    },
    fixedLayoutCls: 'x-table-fixed',
    updateFixedLayout: function(fixedLayout) {
        this.innerElement[fixedLayout ? 'addCls' : 'removeCls'](this.fixedLayoutCls);
    }
});

Ext.define('Ext.tip.ToolTip', {});


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.Droppable', {
    mixins: {
        observable: Ext.mixin.Observable
    },
    config: {
        
        baseCls: Ext.baseCSSPrefix + 'droppable'
    },
    
    activeCls: Ext.baseCSSPrefix + 'drop-active',
    
    invalidCls: Ext.baseCSSPrefix + 'drop-invalid',
    
    hoverCls: Ext.baseCSSPrefix + 'drop-hover',
    
    validDropMode: 'intersect',
    
    disabled: false,
    
    group: 'base',
    
    tolerance: null,
    
    monitoring: false,
    
    constructor: function(el, config) {
        var me = this;
        config = config || {};
        Ext.apply(me, config);
        
        
        
        
        
        me.el = Ext.get(el);
        me.callParent();
        me.mixins.observable.constructor.call(me);
        if (!me.disabled) {
            me.enable();
        }
        me.el.addCls(me.baseCls);
    },
    
    onDragStart: function(draggable, e) {
        if (draggable.group === this.group) {
            this.monitoring = true;
            this.el.addCls(this.activeCls);
            this.region = this.el.getBox(true);
            draggable.on({
                drag: this.onDrag,
                beforedragend: this.onBeforeDragEnd,
                dragend: this.onDragEnd,
                scope: this
            });
            if (this.isDragOver(draggable)) {
                this.setCanDrop(true, draggable, e);
            }
            this.fireEvent('dropactivate', this, draggable, e);
        } else {
            draggable.on({
                dragend: function() {
                    this.el.removeCls(this.invalidCls);
                },
                scope: this,
                single: true
            });
            this.el.addCls(this.invalidCls);
        }
    },
    
    isDragOver: function(draggable, region) {
        return this.region[this.validDropMode](draggable.region);
    },
    
    onDrag: function(draggable, e) {
        this.setCanDrop(this.isDragOver(draggable), draggable, e);
    },
    
    setCanDrop: function(canDrop, draggable, e) {
        if (canDrop && !this.canDrop) {
            this.canDrop = true;
            this.el.addCls(this.hoverCls);
            this.fireEvent('dropenter', this, draggable, e);
        } else if (!canDrop && this.canDrop) {
            this.canDrop = false;
            this.el.removeCls(this.hoverCls);
            this.fireEvent('dropleave', this, draggable, e);
        }
    },
    
    onBeforeDragEnd: function(draggable, e) {
        draggable.cancelRevert = this.canDrop;
    },
    
    onDragEnd: function(draggable, e) {
        this.monitoring = false;
        this.el.removeCls(this.activeCls);
        draggable.un({
            drag: this.onDrag,
            beforedragend: this.onBeforeDragEnd,
            dragend: this.onDragEnd,
            scope: this
        });
        if (this.canDrop) {
            this.canDrop = false;
            this.el.removeCls(this.hoverCls);
            this.fireEvent('drop', this, draggable, e);
        }
        this.fireEvent('dropdeactivate', this, draggable, e);
    },
    
    enable: function() {
        if (!this.mgr) {
            this.mgr = Ext.util.Observable.observe(Ext.util.Draggable);
        }
        this.mgr.on({
            dragstart: this.onDragStart,
            scope: this
        });
        this.disabled = false;
    },
    
    disable: function() {
        this.mgr.un({
            dragstart: this.onDragStart,
            scope: this
        });
        this.disabled = true;
    },
    
    isDisabled: function() {
        return this.disabled;
    },
    
    isMonitoring: function() {
        return this.monitoring;
    }
});


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);
            }
        }
    }
});
