/*
This file is part of Ext JS 4.2

Copyright (c) 2011-2013 Sencha Inc

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

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

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

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

Build date: 2013-09-18 17:18:59 (940c324ac822b840618a3a8b2b4b873f83a1a9b1)
*/





var Ext = Ext || {};
Ext._startTime = new Date().getTime();
(function() {
    var global = this,
        objectPrototype = Object.prototype,
        toString = objectPrototype.toString,
        enumerables = true,
        enumerablesTest = {toString: 1},
        emptyFn = function () {},
        
        
        callOverrideParent = function () {
            var method = callOverrideParent.caller.caller; 
            return method.$owner.prototype[method.$name].apply(this, arguments);
        },
        i,
        nonWhitespaceRe = /\S/,
        ExtApp,
        iterableRe = /\[object\s*(?:Array|Arguments|\w*Collection|\w*List|HTML\s+document\.all\s+class)\]/;

    Ext.global = global;

    for (i in enumerablesTest) {
        enumerables = null;
    }

    if (enumerables) {
        enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable',
                       'toLocaleString', 'toString', 'constructor'];
    }

    
    Ext.enumerables = enumerables;

    
    Ext.apply = function(object, config, defaults) {
        if (defaults) {
            Ext.apply(object, defaults);
        }

        if (object && config && typeof config === 'object') {
            var i, j, k;

            for (i in config) {
                object[i] = config[i];
            }

            if (enumerables) {
                for (j = enumerables.length; j--;) {
                    k = enumerables[j];
                    if (config.hasOwnProperty(k)) {
                        object[k] = config[k];
                    }
                }
            }
        }

        return object;
    };

    Ext.buildSettings = Ext.apply({
        baseCSSPrefix: 'x-'
    }, Ext.buildSettings || {});

    Ext.apply(Ext, {

        
        name: Ext.sandboxName || 'Ext',

        
        emptyFn: emptyFn,
        
        
        identityFn: function(o) {
            return o;
        },

        
        emptyString: new String(),

        baseCSSPrefix: Ext.buildSettings.baseCSSPrefix,

        
        applyIf: function(object, config) {
            var property;

            if (object) {
                for (property in config) {
                    if (object[property] === undefined) {
                        object[property] = config[property];
                    }
                }
            }

            return object;
        },

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

    Ext.apply(Ext, {

        
        extend: (function() {
            
            var objectConstructor = objectPrototype.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);
                    };
                }


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

        
        override: function (target, overrides) {
            if (target.$isClass) {
                target.override(overrides);
            } else if (typeof target == 'function') {
                Ext.apply(target.prototype, overrides);
            } else {
                var owner = target.self,
                    name, value;

                if (owner && owner.$isClass) { 
                    for (name in overrides) {
                        if (overrides.hasOwnProperty(name)) {
                            value = overrides[name];

                            if (typeof value == 'function') {

                                value.$name = name;
                                value.$owner = owner;
                                value.$previous = target.hasOwnProperty(name)
                                    ? target[name] 
                                    : callOverrideParent; 
                            }

                            target[name] = value;
                        }
                    }
                } else {
                    Ext.apply(target, overrides);
                }
            }

            return target;
        }
    });

    
    Ext.apply(Ext, {

        
        valueFrom: function(value, defaultValue, allowBlank){
            return Ext.isEmpty(value, allowBlank) ? defaultValue : value;
        },

        
        typeOf: function(value) {
            var type,
                typeToString;
            
            if (value === null) {
                return 'null';
            }

            type = typeof value;

            if (type === 'undefined' || type === 'string' || type === 'number' || type === 'boolean') {
                return type;
            }

            typeToString = toString.call(value);

            switch(typeToString) {
                case '[object Array]':
                    return 'array';
                case '[object Date]':
                    return 'date';
                case '[object Boolean]':
                    return 'boolean';
                case '[object Number]':
                    return 'number';
                case '[object RegExp]':
                    return 'regexp';
            }

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

        },

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

        
        isEmpty: function(value, allowEmptyString) {
            return (value === null) || (value === undefined) || (!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]';
        },

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

    Ext.apply(Ext, {

        
        clone: function(item) {
            var type,
                i,
                j,
                k,
                clone,
                key;
            
            if (item === null || item === undefined) {
                return item;
            }

            
            
            
            if (item.nodeType && item.cloneNode) {
                return item.cloneNode(true);
            }

            type = toString.call(item);

            
            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 (Ext.global[uniqueGlobalNamespace] !== undefined);

                Ext.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 me = this,
                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: {
            verbose: emptyFn,
            log: emptyFn,
            info: emptyFn,
            warn: emptyFn,
            error: function(message) {
                throw new Error(message);
            },
            deprecate: emptyFn
        }
    });

    
    Ext.type = Ext.typeOf;
    
    
    
    
    ExtApp = Ext.app;
    if (!ExtApp) {
        ExtApp = Ext.app = {};
    }
    Ext.apply(ExtApp, {
        namespaces: {},
        
        
        collectNamespaces: function(paths) {
            var namespaces = Ext.app.namespaces,
                path;
            
            for (path in paths) {
                if (paths.hasOwnProperty(path)) {
                    namespaces[path] = true;
                }
            }
        },

        
        addNamespaces: function(ns) {
            var namespaces = Ext.app.namespaces,
                i, l;

            if (!Ext.isArray(ns)) {
                ns = [ns];
            }

            for (i = 0, l = ns.length; i < l; i++) {
                namespaces[ns[i]] = true;
            }
        },

        
        clearNamespaces: function() {
            Ext.app.namespaces = {};
        },

        
        getNamespace: function(className) {
            var namespaces    = Ext.app.namespaces,
                deepestPrefix = '',
                prefix;

            for (prefix in namespaces) {
                if (namespaces.hasOwnProperty(prefix)    &&
                    prefix.length > deepestPrefix.length &&
                    (prefix + '.' === className.substring(0, prefix.length + 1))) {
                    deepestPrefix = prefix;
                }
            }

            return deepestPrefix === '' ? undefined : deepestPrefix;
        }
    });
}());


Ext.globalEval = Ext.global.execScript
    ? function(code) {
        execScript(code);
    }
    : function($$code) {
        
        
        (function(){
            
            
            
            var Ext = this.Ext;
            eval($$code);
        }());
    };






(function() {



var version = '4.2.2.1144',
    
    checkVerTemp = [''],
    endOfVersionRe = /([^\d\.])/,
    notDigitsRe = /[^\d]/g,
    plusMinusRe = /[\-+]/g,
    stripRe = /\s/g,
    underscoreRe = /_/g,
    Version;

    Ext.Version = Version = Ext.extend(Object, {
        isVersion: true,

        padModes: {
            '~': NaN,
            '^': Infinity
        },

        
        release: '',

        
        constructor: function (version, defaultMode) {
            var me = this,
                padModes = me.padModes,
                ch, i, pad, parts, release, releaseStartIndex, ver;

            if (version.isVersion) {
                return 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;
        },

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

    
    Ext.apply(Ext, {
        
        versions: {},

        
        lastRegisteredVersion: null,

        
        setVersion: function(packageName, version) {
            Ext.lastRegisteredVersion = Ext.versions[packageName] = new Version(version);
            return this;
        },

        
        getVersion: function(packageName) {
            if (packageName === undefined) {
                return Ext.lastRegisteredVersion;
            }

            return Ext.versions[packageName];
        },

        
        checkVersion: function (specs, matchAll) {
            var isArray = Ext.isArray(specs),
                compat = isArray ? specs : checkVerTemp,
                length = compat.length,
                versions = Ext.versions,
                frameworkVer = versions.ext || versions.touch,
                i, index, matches, minVer, maxVer, 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 {
                        if (!(ver = versions[spec.substring(0, index)])) {
                            
                            
                            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);
            }
        }
    }); 

    Ext.setVersion('core', version);

}());







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,
        formatRe      = /\{(\d+)\}/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; 
        };

    return {
        
        
        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) {
            return string.replace(trimRegex, "");
        },

        
        capitalize: function(string) {
            return string.charAt(0).toUpperCase() + string.substr(1);
        },

        
        uncapitalize: function(string) {
            return string.charAt(0).toLowerCase() + string.substr(1);
        },

        
        ellipsis: function(value, len, word) {
            if (value && value.length > len) {
                if (word) {
                    var vs = value.substr(0, len - 2),
                    index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
                    if (index !== -1 && index >= (len - 15)) {
                        return vs.substr(0, index) + "...";
                    }
                }
                return value.substr(0, len - 3) + "...";
            }
            return value;
        },

        
        escapeRegex: function(string) {
            return string.replace(escapeRegexRe, "\\$1");
        },

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

        
        format: function(format) {
            var args = Ext.Array.toArray(arguments, 1);
            return format.replace(formatRe, function(m, i) {
                return args[i];
            });
        },

        
        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.Number = new function() {

    var me = this,
        isToFixedBroken = (0.9).toFixed() !== '1',
        math = Math;

    Ext.apply(this, {
        
        constrain: function(number, min, max) {
            var x = parseFloat(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 me.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 = me.snapInRange(maxValue, increment, minValue))) {
                    value = maxValue;
                }
            }

            return value;
        },

        
        toFixed: isToFixedBroken ? function(value, precision) {
            precision = precision || 0;
            var pow = math.pow(10, precision);
            return (math.round(value * pow) / pow).toFixed(precision);
        } : function(value, precision) {
            return value.toFixed(precision);
        },

        
        from: function(value, defaultValue) {
            if (isFinite(value)) {
                value = parseFloat(value);
            }

            return !isNaN(value) ? value : defaultValue;
        },

        
        randomInt: function (from, to) {
           return math.floor(math.random() * (to - from + 1) + from);
        },
        
        
        correctFloat: function(n) {
            
            
            
            return parseFloat(n.toPrecision(14));
        }
    });

    
    Ext.num = function() {
        return me.from.apply(this, arguments);
    };
};






(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;
        }()),
        supportsForEach = 'forEach' in arrayPrototype,
        supportsMap = 'map' in arrayPrototype,
        supportsIndexOf = 'indexOf' in arrayPrototype,
        supportsEvery = 'every' in arrayPrototype,
        supportsSome = 'some' in arrayPrototype,
        supportsFilter = 'filter' in arrayPrototype,
        supportsSort = (function() {
            var a = [1,2,3,4,5].sort(function(){ return 0; });
            return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
        }()),
        supportsSliceOnNodeList = true,
        ExtArray,
        erase,
        replace,
        splice;

    try {
        
        if (typeof document !== 'undefined') {
            slice.call(document.getElementsByTagName('body'));
        }
    } catch (e) {
        supportsSliceOnNodeList = false;
    }

    function fixArrayIndex (array, index) {
        return (index < 0) ? Math.max(0, array.length + index)
                           : Math.min(array.length, index);
    }

    
    function replaceSim (array, index, removeCount, insert) {
        var add = insert ? insert.length : 0,
            length = array.length,
            pos = fixArrayIndex(array, index),
            remove,
            tailOldPos,
            tailNewPos,
            tailCount,
            lengthAfterRemove,
            i;

        
        if (pos === length) {
            if (add) {
                array.push.apply(array, insert);
            }
        } else {
            remove = Math.min(removeCount, length - pos);
            tailOldPos = pos + remove;
            tailNewPos = tailOldPos + add - remove;
            tailCount = length - tailOldPos;
            lengthAfterRemove = length - remove;

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

    function replaceNative (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;
    }

    function eraseSim (array, index, removeCount) {
        return replaceSim(array, index, removeCount);
    }

    function eraseNative (array, index, removeCount) {
        array.splice(index, removeCount);
        return array;
    }

    function spliceSim (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;
    }

    function spliceNative (array) {
        return array.splice.apply(array, slice.call(arguments, 1));
    }

    erase = supportsSplice ? eraseNative : eraseSim;
    replace = supportsSplice ? replaceNative : replaceSim;
    splice = supportsSplice ? spliceNative : spliceSim;

    

    ExtArray = Ext.Array = {
        
        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: supportsForEach ? function(array, fn, scope) {
            array.forEach(fn, scope);
        } : function(array, fn, scope) {
            var i = 0,
                ln = array.length;

            for (; 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: supportsMap ? function(array, fn, scope) {
            return array.map(fn, scope);
        } : function(array, fn, scope) {
            var results = [],
                i = 0,
                len = array.length;

            for (; i < len; i++) {
                results[i] = fn.call(scope, array[i], i, array);
            }

            return results;
        },

        
        every: supportsEvery ? function(array, fn, scope) {
            return array.every(fn, scope);
        } : function(array, fn, scope) {
            var i = 0,
                ln = array.length;

            for (; i < ln; ++i) {
                if (!fn.call(scope, array[i], i, array)) {
                    return false;
                }
            }

            return true;
        },

        
        some: supportsSome ? function(array, fn, scope) {
            return array.some(fn, scope);
        } : function(array, fn, scope) {
            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: supportsFilter ? function(array, fn, scope) {
            return array.filter(fn, scope);
        } : function(array, fn, scope) {
            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;
        },

        
        include: function(array, item) {
            if (!ExtArray.contains(array, item)) {
                array.push(item);
            }
        },

        
        clone: function(array) {
            return slice.call(array);
        },

        
        merge: function() {
            var args = slice.call(arguments),
                array = [],
                i, ln;

            for (i = 0, ln = args.length; i < ln; i++) {
                array = array.concat(args[i]);
            }

            return ExtArray.unique(array);
        },

        
        intersect: function() {
            var intersection = [],
                arrays = slice.call(arguments),
                arraysLength,
                array,
                arrayLength,
                minArray,
                minArrayIndex,
                minArrayCandidate,
                minArrayLength,
                element,
                elementCandidate,
                elementCount,
                i, j, k;

            if (!arrays.length) {
                return intersection;
            }

            
            arraysLength = arrays.length;
            for (i = minArrayIndex = 0; i < arraysLength; i++) {
                minArrayCandidate = arrays[i];
                if (!minArray || minArrayCandidate.length < minArray.length) {
                    minArray = minArrayCandidate;
                    minArrayIndex = i;
                }
            }

            minArray = ExtArray.unique(minArray);
            erase(arrays, minArrayIndex, 1);

            
            
            
            minArrayLength = minArray.length;
            arraysLength = arrays.length;
            for (i = 0; i < minArrayLength; i++) {
                element = minArray[i];
                elementCount = 0;

                for (j = 0; j < arraysLength; j++) {
                    array = arrays[j];
                    arrayLength = array.length;
                    for (k = 0; k < arrayLength; k++) {
                        elementCandidate = array[k];
                        if (element === elementCandidate) {
                            elementCount++;
                            break;
                        }
                    }
                }

                if (elementCount === arraysLength) {
                    intersection.push(element);
                }
            }

            return intersection;
        },

        
        difference: function(arrayA, arrayB) {
            var clone = slice.call(arrayA),
                ln = clone.length,
                i, j, lnB;

            for (i = 0,lnB = arrayB.length; i < lnB; i++) {
                for (j = 0; j < ln; j++) {
                    if (clone[j] === arrayB[i]) {
                        erase(clone, j, 1);
                        j--;
                        ln--;
                    }
                }
            }

            return clone;
        },

        
        
        slice: ([1,2].slice(1, undefined).length ?
            function (array, begin, end) {
                return slice.call(array, begin, end);
            } :
            
            function (array, begin, end) {
                
                
                if (typeof begin === 'undefined') {
                    return slice.call(array);
                }
                if (typeof end === 'undefined') {
                    return slice.call(array, begin);
                }
                return slice.call(array, begin, end);
            }
        ),

        
        sort: supportsSort ? function(array, sortFn) {
            if (sortFn) {
                return array.sort(sortFn);
            } else {
                return array.sort();
            }
         } : function(array, sortFn) {
            var length = array.length,
                i = 0,
                comparison,
                j, min, tmp;

            for (; i < length; i++) {
                min = i;
                for (j = i + 1; j < length; j++) {
                    if (sortFn) {
                        comparison = sortFn(array[j], array[min]);
                        if (comparison < 0) {
                            min = j;
                        }
                    } else if (array[j] < array[min]) {
                        min = j;
                    }
                }
                if (min !== i) {
                    tmp = array[i];
                    array[i] = array[min];
                    array[min] = tmp;
                }
            }

            return array;
        },

        
        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) {
            var map = {},
                i = array.length;

            if (!getKey) {
                while (i--) {
                    map[array[i]] = array[i];
                }
            } else if (typeof getKey == 'string') {
                while (i--) {
                    map[array[i][getKey]] = array[i];
                }
            } else {
                while (i--) {
                    map[getKey.call(scope, array[i])] = array[i];
                }
            }

            return map;
        },


        
        erase: erase,

        
        insert: function (array, index, items) {
            return replace(array, index, 0, items);
        },

        
        replace: replace,

        
        splice: splice,

        
        push: function(array) {
            var len = arguments.length,
                i = 1,
                newItem;

            if (array === undefined) {
                array = [];
            } else if (!Ext.isArray(array)) {
                array = [array];
            }
            for (; i < len; i++) {
                newItem = arguments[i];
                Array.prototype.push[Ext.isIterable(newItem) ? 'apply' : 'call'](array, newItem);
            }
            return array;
        }
    };

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






Ext.Function = {

    
    flexSetter: function(fn) {
        return function(a, b) {
            var k, i;

            if (a === null) {
                return this;
            }

            if (typeof a !== 'string') {
                for (k in a) {
                    if (a.hasOwnProperty(k)) {
                        fn.call(this, k, a[k]);
                    }
                }

                if (Ext.enumerables) {
                    for (i = Ext.enumerables.length; i--;) {
                        k = Ext.enumerables[i];
                        if (a.hasOwnProperty(k)) {
                            fn.call(this, k, a[k]);
                        }
                    }
                }
            } else {
                fn.call(this, a, b);
            }

            return this;
        };
    },

    
    bind: function(fn, scope, args, appendArgs) {
        if (arguments.length === 2) {
            return function() {
                return fn.apply(scope, arguments);
            };
        }

        var method = fn,
            slice = Array.prototype.slice;

        return function() {
            var callArgs = args || arguments;

            if (appendArgs === true) {
                callArgs = slice.call(arguments, 0);
                callArgs = callArgs.concat(args);
            }
            else if (typeof appendArgs == 'number') {
                callArgs = slice.call(arguments, 0); 
                Ext.Array.insert(callArgs, appendArgs, args);
            }

            return method.apply(scope || Ext.global, callArgs);
        };
    },

    
    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 = [].concat(args);
            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) {
        var method = origFn;
        if (!Ext.isFunction(newFn)) {
            return origFn;
        } else {
            returnValue = Ext.isDefined(returnValue) ? returnValue : null;
            return function() {
                var me = this,
                    args = arguments;
                    
                newFn.target = me;
                newFn.method = origFn;
                return (newFn.apply(scope || me || Ext.global, args) !== false) ? origFn.apply(me || Ext.global, args) : returnValue;
            };
        }
    },

    
    createDelayed: function(fn, delay, scope, args, appendArgs) {
        if (scope || args) {
            fn = Ext.Function.bind(fn, scope, args, appendArgs);
        }

        return function() {
            var me = this,
                args = Array.prototype.slice.call(arguments);

            setTimeout(function() {
                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(Ext.supports.TimeoutActualLateness ? function () {
                fn();
            } : fn, millis);
        }
        fn();
        return 0;
    },

    
    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(){
                fn.apply(me, callArgs);
            }, buffer);
        };
    },

    
    createThrottled: function(fn, interval, scope) {
        var lastCallTime, elapsed, lastArgs, timer, execute = function() {
            fn.apply(scope || this, lastArgs);
            lastCallTime = Ext.Date.now();
        };

        return function() {
            elapsed = Ext.Date.now() - lastCallTime;
            lastArgs = arguments;

            clearTimeout(timer);
            if (!lastCallTime || (elapsed >= interval)) {
                execute();
            } else {
                timer = setTimeout(execute, interval - elapsed);
            }
        };
    },


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


Ext.defer = Ext.Function.alias(Ext.Function, 'defer');


Ext.pass = Ext.Function.alias(Ext.Function, 'pass');


Ext.bind = Ext.Function.alias(Ext.Function, 'bind');







(function() {


var TemplateClass = function(){},
    ExtObject = Ext.Object = {

    
    chain: Object.create || function (object) {
        TemplateClass.prototype = object;
        var result = new TemplateClass();
        TemplateClass.prototype = null;
        return result;
    },

    
    clear: function (object) {
        var keys = ExtObject.getKeys(object),
            n = keys.length;

        while (n--) {
            delete object[keys[n]];
        }

        return object;
    },

    
    toQueryObjects: function(name, value, recursive) {
        var self = ExtObject.toQueryObjects,
            objects = [],
            i, ln;

        if (Ext.isArray(value)) {
            for (i = 0, ln = value.length; i < ln; i++) {
                if (recursive) {
                    objects = objects.concat(self(name + '[' + i + ']', value[i], true));
                }
                else {
                    objects.push({
                        name: name,
                        value: value[i]
                    });
                }
            }
        }
        else if (Ext.isObject(value)) {
            for (i in value) {
                if (value.hasOwnProperty(i)) {
                    if (recursive) {
                        objects = objects.concat(self(name + '[' + i + ']', value[i], true));
                    }
                    else {
                        objects.push({
                            name: name,
                            value: value[i]
                        });
                    }
                }
            }
        }
        else {
            objects.push({
                name: name,
                value: value
            });
        }

        return objects;
    },

    
    toQueryString: function(object, recursive) {
        var paramObjects = [],
            params = [],
            i, j, ln, paramObject, value;

        for (i in object) {
            if (object.hasOwnProperty(i)) {
                paramObjects = paramObjects.concat(ExtObject.toQueryObjects(i, object[i], recursive));
            }
        }

        for (j = 0, ln = paramObjects.length; j < ln; j++) {
            paramObject = paramObjects[j];
            value = paramObject.value;

            if (Ext.isEmpty(value)) {
                value = '';
            } else if (Ext.isDate(value)) {
                value = Ext.Date.toString(value);
            }

            params.push(encodeURIComponent(paramObject.name) + '=' + encodeURIComponent(String(value)));
        }

        return params.join('&');
    },

    
    fromQueryString: function(queryString, recursive) {
        var parts = queryString.replace(/^\?/, '').split('&'),
            object = {},
            temp, components, name, value, i, ln,
            part, j, subLn, matchedKeys, matchedName,
            keys, key, nextKey;

        for (i = 0, ln = parts.length; i < ln; i++) {
            part = parts[i];

            if (part.length > 0) {
                components = part.split('=');
                name = decodeURIComponent(components[0]);
                value = (components[1] !== undefined) ? decodeURIComponent(components[1]) : '';

                if (!recursive) {
                    if (object.hasOwnProperty(name)) {
                        if (!Ext.isArray(object[name])) {
                            object[name] = [object[name]];
                        }

                        object[name].push(value);
                    }
                    else {
                        object[name] = value;
                    }
                }
                else {
                    matchedKeys = name.match(/(\[):?([^\]]*)\]/g);
                    matchedName = name.match(/^([^\[]+)/);


                    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) {
        for (var property in object) {
            if (object.hasOwnProperty(property)) {
                if (fn.call(scope || object, property, object[property], object) === 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;
    },

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

    
    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.urlEncode = function() {
    var args = Ext.Array.from(arguments),
        prefix = '';

    
    if ((typeof args[1] === 'string')) {
        prefix = args[1] + '&';
        args[1] = false;
    }

    return prefix + ExtObject.toQueryString.apply(ExtObject, args);
};


Ext.urlDecode = function() {
    return ExtObject.fromQueryString.apply(ExtObject, arguments);
};

}());









Ext.Date = new function() {
  var utilDate = this,
      stripEscapeRe = /(\\.)/g,
      hourInfoRe = /([gGhHisucUOPZ]|MS)/,
      dateInfoRe = /([djzmnYycU]|MS)/,
      slashRe = /\\/gi,
      numberTokenRe = /\{(\d+)\}/g,
      MSFormatRe = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/'),
      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),",
                        "week1monday = new Date(jan4.getTime() - ((jan4.getDay() - 1) * 86400000));",
                        "v = Ext.Date.clearTime(new Date(week1monday.getTime() + ((W - 1) * 604800000)));",
                    "} else {",
                        
                        
                        "v = me.add(new Date(y < 100 ? 100 : y, m, d, h, i, s, ms), me.YEAR, y < 100 ? y - 100 : 0);",
                    "}",
                "}",
            "}",
        "}",

        "if(v){",
            
            "if(zz != null){",
                
                "v = me.add(v, me.SECOND, -v.getTimezoneOffset() * 60 - zz);",
            "}else if(o){",
                
                "v = me.add(v, me.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
            "}",
        "}",

        "return v;"
      ].join('\n');

  
  
  
  function xf(format) {
      var args = Array.prototype.slice.call(arguments, 1);
      return format.replace(numberTokenRe, function(m, i) {
          return args[i];
      });
  }

  Ext.apply(utilDate, {
    
    now: Date.now || function() {
        return +new Date();
    },

    
    toString: function(date) {
        var pad = Ext.String.leftPad;

        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 Date(((r[1] || '') + r[2]) * 1) : null;
        },
        "time": function(input, strict) {
            var num = parseInt(input, 10);
            if (num || num === 0) {
                return new Date(num);
            }
            return null;
        },
        "timestamp": function(input, strict) {
            var num = parseInt(input, 10);
            if (num || num === 0) {
                return new Date(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 Ext.Date.monthNames[month].substring(0, 3);
    },
    

    
    
    getShortDayName : function(day) {
        return Ext.Date.dayNames[day].substring(0, 3);
    },
    

    
    
    getMonthNumber : function(name) {
        
        return Ext.Date.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(this.getDate(), 2, '0')",
        D: "Ext.Date.getShortDayName(this.getDay())", 
        j: "this.getDate()",
        l: "Ext.Date.dayNames[this.getDay()]",
        N: "(this.getDay() ? this.getDay() : 7)",
        S: "Ext.Date.getSuffix(this)",
        w: "this.getDay()",
        z: "Ext.Date.getDayOfYear(this)",
        W: "Ext.String.leftPad(Ext.Date.getWeekOfYear(this), 2, '0')",
        F: "Ext.Date.monthNames[this.getMonth()]",
        m: "Ext.String.leftPad(this.getMonth() + 1, 2, '0')",
        M: "Ext.Date.getShortMonthName(this.getMonth())", 
        n: "(this.getMonth() + 1)",
        t: "Ext.Date.getDaysInMonth(this)",
        L: "(Ext.Date.isLeapYear(this) ? 1 : 0)",
        o: "(this.getFullYear() + (Ext.Date.getWeekOfYear(this) == 1 && this.getMonth() > 0 ? +1 : (Ext.Date.getWeekOfYear(this) >= 52 && this.getMonth() < 11 ? -1 : 0)))",
        Y: "Ext.String.leftPad(this.getFullYear(), 4, '0')",
        y: "('' + this.getFullYear()).substring(2, 4)",
        a: "(this.getHours() < 12 ? 'am' : 'pm')",
        A: "(this.getHours() < 12 ? 'AM' : 'PM')",
        g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
        G: "this.getHours()",
        h: "Ext.String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
        H: "Ext.String.leftPad(this.getHours(), 2, '0')",
        i: "Ext.String.leftPad(this.getMinutes(), 2, '0')",
        s: "Ext.String.leftPad(this.getSeconds(), 2, '0')",
        u: "Ext.String.leftPad(this.getMilliseconds(), 3, '0')",
        O: "Ext.Date.getGMTOffset(this)",
        P: "Ext.Date.getGMTOffset(this, true)",
        T: "Ext.Date.getTimezone(this)",
        Z: "(this.getTimezoneOffset() * -60)",

        c: function() { 
            var c, code, i, l, e;
            for (c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
                e = c.charAt(i);
                code.push(e == "T" ? "'T'" : utilDate.getFormatCode(e)); 
            }
            return code.join(" + ");
        },
        

        U: "Math.round(this.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 Date(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 {
                code.push(utilDate.getFormatCode(ch));
            }
        }
        utilDate.formatFunctions[format] = Ext.functionFactory("return " + code.join('+'));
    },

    
    createParser : function(format) {
        var regexNum = utilDate.parseRegexes.length,
            currentGroup = 1,
            calc = [],
            regex = [],
            special = false,
            ch = "",
            i = 0,
            len = format.length,
            atEnd = [],
            obj;

        for (; i < len; ++i) {
            ch = format.charAt(i);
            if (!special && ch == "\\") {
                special = true;
            } else if (special) {
                special = false;
                regex.push(Ext.String.escape(ch));
            } else {
                obj = utilDate.formatCodeToRegex(ch, currentGroup);
                currentGroup += obj.g;
                regex.push(obj.s);
                if (obj.g && obj.c) {
                    if (obj.calcAtEnd) {
                        atEnd.push(obj.c);
                    } else {
                        calc.push(obj.c);
                    }
                }
            }
        }

        calc = calc.concat(atEnd);

        utilDate.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", 'i');
        utilDate.parseFunctions[format] = Ext.functionFactory("input", "strict", xf(code, regexNum, calc.join('')));
    },

    
    parseCodes : {
        
        d: {
            g:1,
            c:"d = parseInt(results[{0}], 10);\n",
            s:"(3[0-1]|[1-2][0-9]|0[1-9])" 
        },
        j: {
            g:1,
            c:"d = parseInt(results[{0}], 10);\n",
            s:"(3[0-1]|[1-2][0-9]|[1-9])" 
        },
        D: function() {
            for (var a = [], i = 0; i < 7; a.push(utilDate.getShortDayName(i)), ++i); 
            return {
                g:0,
                c:null,
                s:"(?:" + a.join("|") +")"
            };
        },
        l: function() {
            return {
                g:0,
                c:null,
                s:"(?:" + utilDate.dayNames.join("|") + ")"
            };
        },
        N: {
            g:0,
            c:null,
            s:"[1-7]" 
        },
        
        S: {
            g:0,
            c:null,
            s:"(?:st|nd|rd|th)"
        },
        
        w: {
            g:0,
            c:null,
            s:"[0-6]" 
        },
        z: {
            g:1,
            c:"z = parseInt(results[{0}], 10);\n",
            s:"(\\d{1,3})" 
        },
        W: {
            g:1,
            c:"W = parseInt(results[{0}], 10);\n",
            s:"(\\d{2})" 
        },
        F: function() {
            return {
                g:1,
                c:"m = parseInt(me.getMonthNumber(results[{0}]), 10);\n", 
                s:"(" + utilDate.monthNames.join("|") + ")"
            };
        },
        M: function() {
            for (var a = [], i = 0; i < 12; a.push(utilDate.getShortMonthName(i)), ++i); 
            return Ext.applyIf({
                s:"(" + a.join("|") + ")"
            }, utilDate.formatCodeToRegex("F"));
        },
        m: {
            g:1,
            c:"m = parseInt(results[{0}], 10) - 1;\n",
            s:"(1[0-2]|0[1-9])" 
        },
        n: {
            g:1,
            c:"m = parseInt(results[{0}], 10) - 1;\n",
            s:"(1[0-2]|[1-9])" 
        },
        t: {
            g:0,
            c:null,
            s:"(?:\\d{2})" 
        },
        L: {
            g:0,
            c:null,
            s:"(?:1|0)"
        },
        o: { 
            g: 1,
            c: "y = parseInt(results[{0}], 10);\n",
            s: "(\\d{4})" 

        },
        Y: {
            g:1,
            c:"y = parseInt(results[{0}], 10);\n",
            s:"(\\d{4})" 
        },
        y: {
            g:1,
            c:"var ty = parseInt(results[{0}], 10);\n"
                + "y = ty > me.y2kYear ? 1900 + ty : 2000 + ty;\n", 
            s:"(\\d{1,2})"
        },
        
        
        a: {
            g:1,
            c:"if (/(am)/i.test(results[{0}])) {\n"
                + "if (!h || h == 12) { h = 0; }\n"
                + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
            s:"(am|pm|AM|PM)",
            calcAtEnd: true
        },
        
        
        A: {
            g:1,
            c:"if (/(am)/i.test(results[{0}])) {\n"
                + "if (!h || h == 12) { h = 0; }\n"
                + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
            s:"(AM|PM|am|pm)",
            calcAtEnd: true
        },
        
        g: {
            g:1,
            c:"h = parseInt(results[{0}], 10);\n",
            s:"(1[0-2]|[0-9])" 
        },
        G: {
            g:1,
            c:"h = parseInt(results[{0}], 10);\n",
            s:"(2[0-3]|1[0-9]|[0-9])" 
        },
        h: {
            g:1,
            c:"h = parseInt(results[{0}], 10);\n",
            s:"(1[0-2]|0[1-9])" 
        },
        H: {
            g:1,
            c:"h = parseInt(results[{0}], 10);\n",
            s:"(2[0-3]|[0-1][0-9])" 
        },
        i: {
            g:1,
            c:"i = parseInt(results[{0}], 10);\n",
            s:"([0-5][0-9])" 
        },
        s: {
            g:1,
            c:"s = parseInt(results[{0}], 10);\n",
            s:"([0-5][0-9])" 
        },
        u: {
            g:1,
            c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
            s:"(\\d+)" 
        },
        O: {
            g:1,
            c:[
                "o = results[{0}];",
                "var sn = o.substring(0,1),", 
                    "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", 
                    "mn = o.substring(3,5) % 60;", 
                "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n" 
            ].join("\n"),
            s: "([+-]\\d{4})" 
        },
        P: {
            g:1,
            c:[
                "o = results[{0}];",
                "var sn = o.substring(0,1),", 
                    "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", 
                    "mn = o.substring(4,6) % 60;", 
                "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n" 
            ].join("\n"),
            s: "([+-]\\d{2}:\\d{2})" 
        },
        T: {
            g:0,
            c:null,
            s:"[A-Z]{1,5}" 
        },
        Z: {
            g:1,
            c:"zz = results[{0}] * 1;\n" 
                  + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
            s:"([+-]?\\d{1,5})" 
        },
        c: function() {
            var calc = [],
                arr = [
                    utilDate.formatCodeToRegex("Y", 1), 
                    utilDate.formatCodeToRegex("m", 2), 
                    utilDate.formatCodeToRegex("d", 3), 
                    utilDate.formatCodeToRegex("H", 4), 
                    utilDate.formatCodeToRegex("i", 5), 
                    utilDate.formatCodeToRegex("s", 6), 
                    {c:"ms = results[7] || '0'; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n"}, 
                    {c:[ 
                        "if(results[8]) {", 
                            "if(results[8] == 'Z'){",
                                "zz = 0;", 
                            "}else if (results[8].indexOf(':') > -1){",
                                utilDate.formatCodeToRegex("P", 8).c, 
                            "}else{",
                                utilDate.formatCodeToRegex("O", 8).c, 
                            "}",
                        "}"
                    ].join('\n')}
                ],
                i,
                l;

            for (i = 0, l = arr.length; i < l; ++i) {
                calc.push(arr[i].c);
            }

            return {
                g:1,
                c:calc.join(""),
                s:[
                    arr[0].s, 
                    "(?:", "-", arr[1].s, 
                        "(?:", "-", arr[2].s, 
                            "(?:",
                                "(?:T| )?", 
                                arr[3].s, ":", arr[4].s,  
                                "(?::", arr[5].s, ")?", 
                                "(?:(?:\\.|,)(\\d+))?", 
                                "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", 
                            ")?",
                        ")?",
                    ")?"
                ].join("")
            };
        },
        U: {
            g:1,
            c:"u = parseInt(results[{0}], 10);\n",
            s:"(-?\\d+)" 
        }
    },

    
    
    dateFormat: function(date, format) {
        return utilDate.format(date, format);
    },

    
    isEqual: function(date1, date2) {
        
        if (date1 && date2) {
            return (date1.getTime() === date2.getTime());
        }
        
        return !(date1 || date2);
    },

    
    format: function(date, format) {
        var formatFunctions = utilDate.formatFunctions;

        if (!Ext.isDate(date)) {
            return '';
        }

        if (formatFunctions[format] == null) {
            utilDate.createFormat(format);
        }

        return formatFunctions[format].call(date) + '';
    },

    
    getTimezone : function(date) {
        
        
        
        
        
        
        
        
        
        
        
        
        return date.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,5})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
    },

    
    getGMTOffset : function(date, colon) {
        var offset = date.getTimezoneOffset();
        return (offset > 0 ? "-" : "+")
            + Ext.String.leftPad(Math.floor(Math.abs(offset) / 60), 2, "0")
            + (colon ? ":" : "")
            + Ext.String.leftPad(Math.abs(offset % 60), 2, "0");
    },

    
    getDayOfYear: function(date) {
        var num = 0,
            d = Ext.Date.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 = 864e5, 
            ms7d = 7 * ms1d; 

        return function(date) { 
            var DC3 = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate() + 3) / ms1d, 
                AWN = Math.floor(DC3 / 7), 
                Wyr = new Date(AWN * ms7d).getUTCFullYear();

            return AWN - Math.floor(Date.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 Date(date.getFullYear(), date.getMonth(), 1);
    },

    
    getLastDateOfMonth : function(date) {
        return new Date(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 Date(date.getTime());
    },

    
    isDST : function(date) {
        
        
        return new Date(date.getFullYear(), 0, 1).getTimezoneOffset() != date.getTimezoneOffset();
    },

    
    clearTime : function(date, clone) {
        if (clone) {
            return Ext.Date.clearTime(Ext.Date.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, Ext.Date.HOUR, hr); c.getDate() != d; hr++, c = utilDate.add(date, Ext.Date.HOUR, hr));

            date.setDate(d);
            date.setHours(c.getHours());
        }

        return date;
    },

    
    add : function(date, interval, value) {
        var d = Ext.Date.clone(date),
            Date = Ext.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 Ext.Date.MILLI:
                    d.setTime(d.getTime() + value);
                    break;
                case Ext.Date.SECOND:
                    d.setTime(d.getTime() + value * 1000);
                    break;
                case Ext.Date.MINUTE:
                    d.setTime(d.getTime() + value * 60 * 1000);
                    break;
                case Ext.Date.HOUR:
                    d.setTime(d.getTime() + value * 60 * 60 * 1000);
                    break;
                case Ext.Date.DAY:
                    d.setDate(d.getDate() + value);
                    break;
                case Ext.Date.MONTH:
                    day = date.getDate();
                    if (day > 28) {
                        day = Math.min(day, Ext.Date.getLastDateOfMonth(Ext.Date.add(Ext.Date.getFirstDateOfMonth(date), Ext.Date.MONTH, value)).getDate());
                    }
                    d.setDate(day);
                    d.setMonth(date.getMonth() + value);
                    break;
                case Ext.Date.YEAR:
                    day = date.getDate();
                    if (day > 28) {
                        day = Math.min(day, Ext.Date.getLastDateOfMonth(Ext.Date.add(Ext.Date.getFirstDateOfMonth(date), Ext.Date.YEAR, value)).getDate());
                    }
                    d.setDate(day);
                    d.setFullYear(date.getFullYear() + value);
                    break;
            }
        }

        if (decimalValue) {
            switch (interval.toLowerCase()) {
                case Ext.Date.MILLI:    base = 1;               break;
                case Ext.Date.SECOND:   base = 1000;            break;
                case Ext.Date.MINUTE:   base = 1000*60;         break;
                case Ext.Date.HOUR:     base = 1000*60*60;      break;
                case Ext.Date.DAY:      base = 1000*60*60*24;   break;

                case Ext.Date.MONTH:
                    day = utilDate.getDaysInMonth(d);
                    base = 1000*60*60*24*day;
                    break;

                case Ext.Date.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 nativeDate = window.Date,
            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);
            };
        }
    }
  });
};






(function(flexSetter) {

var noArgs = [],
    Base = function(){},
    hookFunctionFactory = function(hookFunction, underriddenFunction, methodName, owningClass) {
        var result = function() {
            var result = this.callParent(arguments);
            hookFunction.apply(this, arguments);
            return result;
        };
        result.$name = methodName;
        result.$owner = owningClass;
        if (underriddenFunction) {
            result.$previous = underriddenFunction.$previous;
            underriddenFunction.$previous = result;
        }
        return result;
    };

    
    Ext.apply(Base, {
        $className: 'Ext.Base',

        $isClass: true,

        
        create: function() {
            return Ext.create.apply(Ext, [this].concat(Array.prototype.slice.call(arguments, 0)));
        },

        
        extend: function(parent) {
            var parentPrototype = parent.prototype,
                basePrototype, prototype, i, ln, name, statics;

            prototype = this.prototype = Ext.Object.chain(parentPrototype);
            prototype.self = this;

            this.superclass = prototype.superclass = parentPrototype;

            if (!parent.$isClass) {
                basePrototype = Ext.Base.prototype;

                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 (!this.hasOwnProperty(name)) {
                        this[name] = parent[name];
                    }
                }
            }

            if (parent.$onExtended) {
                this.$onExtended = parent.$onExtended.slice();
            }

            prototype.config = new prototype.configClass();
            prototype.initConfigList = prototype.initConfigList.slice();
            prototype.initConfigMap = Ext.clone(prototype.initConfigMap);
            prototype.configMap = Ext.Object.chain(prototype.configMap);
        },

        
        $onExtended: [],

        
        triggerExtended: function() {
        
            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;
        },

        
        addConfig: function(config, fullMerge) {
            var prototype = this.prototype,
                configNameCache = Ext.Class.configNameCache,
                hasConfig = prototype.configMap,
                initConfigList = prototype.initConfigList,
                initConfigMap = prototype.initConfigMap,
                defaultConfig = prototype.config,
                initializedName, name, value;

            for (name in config) {
                if (config.hasOwnProperty(name)) {
                    if (!hasConfig[name]) {
                        hasConfig[name] = true;
                    }

                    value = config[name];

                    initializedName = configNameCache[name].initialized;

                    if (!initConfigMap[name] && value !== null && !prototype[initializedName]) {
                        initConfigMap[name] = true;
                        initConfigList.push(name);
                    }
                }
            }

            if (fullMerge) {
                Ext.merge(defaultConfig, config);
            }
            else {
                Ext.mergeIf(defaultConfig, config);
            }

            prototype.configClass = Ext.Object.classify(defaultConfig);
        },

        
        addStatics: function(members) {
            var member, name;

            for (name in members) {
                if (members.hasOwnProperty(name)) {
                    member = members[name];
                    if (typeof member == 'function' && !member.$isClass && member !== Ext.emptyFn && member !== Ext.identityFn) {
                        member.$owner = this;
                        member.$name = name;
                    }
                    this[name] = member;
                }
            }

            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 = {};
            }

            for (name in members) {
                if (members.hasOwnProperty(name)) {
                    member = members[name];
                    this[name] = member;

                    if (!hasInheritableStatics[name]) {
                        hasInheritableStatics[name] = true;
                        inheritableStatics.push(name);
                    }
                }
            }

            return this;
        },

        
        addMembers: function(members) {
            var prototype = this.prototype,
                enumerables = Ext.enumerables,
                names = [],
                i, ln, name, member;

            for (name in members) {
                names.push(name);
            }

            if (enumerables) {
                names.push.apply(names, enumerables);
            }

            for (i = 0,ln = names.length; i < ln; i++) {
                name = names[i];

                if (members.hasOwnProperty(name)) {
                    member = members[name];

                    if (typeof member == 'function' && !member.$isClass && member !== Ext.emptyFn && member !== Ext.identityFn) {
                        member.$owner = this;
                        member.$name = name;
                    }

                    prototype[name] = member;
                }
            }

            return this;
        },

        
        addMember: function(name, member) {            
            if (typeof member == 'function' && !member.$isClass && member !== Ext.emptyFn && member !== Ext.identityFn) {
                member.$owner = this;
                member.$name = name;
            }

            this.prototype[name] = member;
            return this;
        },

        
        implement: function() {
            this.addMembers.apply(this, arguments);
        },

        
        borrow: function(fromClass, members) {
            
            var prototype = this.prototype,
                fromPrototype = fromClass.prototype,
                i, ln, name, fn, toBorrow;

            members = Ext.Array.from(members);

            for (i = 0,ln = members.length; i < ln; i++) {
                name = members[i];

                toBorrow = fromPrototype[name];

                if (typeof toBorrow == 'function') {
                    fn = Ext.Function.clone(toBorrow);


                    fn.$owner = this;
                    fn.$name = name;

                    prototype[name] = fn;
                }
                else {
                    prototype[name] = toBorrow;
                }
            }

            return this;
        },

        
        override: function(members) {
            var me = this,
                enumerables = Ext.enumerables,
                target = me.prototype,
                cloneFunction = Ext.Function.clone,
                name, index, member, statics, names, previous;

            if (arguments.length === 2) {
                name = members;
                members = {};
                members[name] = arguments[1];
                enumerables = null;
            }

            do {
                names = []; 
                statics = null; 

                for (name in members) { 
                    if (name == 'statics') {
                        statics = members[name];
                    } else if (name == 'inheritableStatics'){
                        me.addInheritableStatics(members[name]);
                    } else if (name == 'config') {
                        me.addConfig(members[name], true);
                    } else {
                        names.push(name);
                    }
                }

                if (enumerables) {
                    names.push.apply(names, enumerables);
                }

                for (index = names.length; index--; ) {
                    name = names[index];

                    if (members.hasOwnProperty(name)) {
                        member = members[name];

                        if (typeof member == 'function' && !member.$className && member !== Ext.emptyFn && member !== Ext.identityFn) {
                            if (typeof member.$owner != 'undefined') {
                                member = cloneFunction(member);
                            }


                            member.$owner = me;
                            member.$name = name;

                            previous = target.hasOwnProperty(name) && target[name];
                            if (previous) {
                                member.$previous = previous;
                            }
                        }

                        target[name] = member;
                    }
                }

                target = me; 
                members = statics; 
            } while (members);

            return this;
        },

        
        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 = mixinClass.prototype,
                prototype = me.prototype,
                key, statics, i, ln, staticName,
                mixinValue, hookKey, hookFunction;

            if (typeof mixin.onClassMixedIn != 'undefined') {
                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.merge(prototype.mixins, mixinValue);
                }
                else if (key === 'xhooks') {
                    for (hookKey in mixinValue) {
                        hookFunction = mixinValue[hookKey];

                        
                        hookFunction.$previous = Ext.emptyFn;

                        if (prototype.hasOwnProperty(hookKey)) {

                            
                            
                            
                            hookFunctionFactory(hookFunction, prototype[hookKey], hookKey, me);
                        } else {
                            
                            
                            prototype[hookKey] = hookFunctionFactory(hookFunction, null, hookKey, me);
                        }
                    }
                }
                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, false);
            }

            prototype.mixins[name] = mixin;
            return me;
        },

        
        getName: function() {
            return Ext.getClassName(this);
        },

        
        createAlias: flexSetter(function(alias, origin) {
            this.override(alias, function() {
                return this[origin].apply(this, arguments);
            });
        }),

        
        addXtype: function(xtype) {
            var prototype = this.prototype,
                xtypesMap = prototype.xtypesMap,
                xtypes = prototype.xtypes,
                xtypesChain = prototype.xtypesChain;

            if (!prototype.hasOwnProperty('xtypesMap')) {
                xtypesMap = prototype.xtypesMap = Ext.merge({}, prototype.xtypesMap || {});
                xtypes = prototype.xtypes = prototype.xtypes ? [].concat(prototype.xtypes) : [];
                xtypesChain = prototype.xtypesChain = prototype.xtypesChain ? [].concat(prototype.xtypesChain) : [];
                prototype.xtype = xtype;
            }

            if (!xtypesMap[xtype]) {
                xtypesMap[xtype] = true;
                xtypes.push(xtype);
                xtypesChain.push(xtype);
                Ext.ClassManager.setAlias(this, 'widget.' + xtype);
            }

            return this;
        }
    });

    Base.implement({
        
        isInstance: true,

        
        $className: 'Ext.Base',

        
        configClass: Ext.emptyFn,

        
        initConfigList: [],

        
        configMap: {},

        
        initConfigMap: {},

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


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


            return superMethod.apply(this, args || noArgs);
        },

        
        self: Base,

        
        constructor: function() {
            return this;
        },

        
        initConfig: function(config) {
            var instanceConfig = config,
                configNameCache = Ext.Class.configNameCache,
                defaultConfig = new this.configClass(),
                defaultConfigList = this.initConfigList,
                hasConfig = this.configMap,
                nameMap, i, ln, name, initializedName;

            this.initConfig = Ext.emptyFn;

            this.initialConfig = instanceConfig || {};

            this.config = config = (instanceConfig) ? Ext.merge(defaultConfig, config) : defaultConfig;

            if (instanceConfig) {
                defaultConfigList = defaultConfigList.slice();

                for (name in instanceConfig) {
                    if (hasConfig[name]) {
                        if (instanceConfig[name] !== null) {
                            defaultConfigList.push(name);
                            this[configNameCache[name].initialized] = false;
                        }
                    }
                }
            }

            for (i = 0,ln = defaultConfigList.length; i < ln; i++) {
                name = defaultConfigList[i];
                nameMap = configNameCache[name];
                initializedName = nameMap.initialized;

                if (!this[initializedName]) {
                    this[initializedName] = true;
                    this[nameMap.set].call(this, config[name]);
                }
            }

            return this;
        },

        
        hasConfig: function(name) {
            return Boolean(this.configMap[name]);
        },

        
        setConfig: function(config, applyIfNotSet) {
            if (!config) {
                return this;
            }

            var configNameCache = Ext.Class.configNameCache,
                currentConfig = this.config,
                hasConfig = this.configMap,
                initialConfig = this.initialConfig,
                name, value;

            applyIfNotSet = Boolean(applyIfNotSet);

            for (name in config) {
                if (applyIfNotSet && initialConfig.hasOwnProperty(name)) {
                    continue;
                }

                value = config[name];
                currentConfig[name] = value;

                if (hasConfig[name]) {
                    this[configNameCache[name].set](value);
                }
            }

            return this;
        },

        
        getConfig: function(name) {
            var configNameCache = Ext.Class.configNameCache;

            return this[configNameCache[name].get]();
        },

        
        getInitialConfig: function(name) {
            var config = this.config;

            if (!name) {
                return config;
            }
            else {
                return config[name];
            }
        },

        
        onConfigUpdate: function(names, callback, scope) {
            var self = this.self,
                i, ln, name,
                updaterName, updater, newUpdater;

            names = Ext.Array.from(names);

            scope = scope || this;

            for (i = 0,ln = names.length; i < ln; i++) {
                name = names[i];
                updaterName = 'update' + Ext.String.capitalize(name);
                updater = this[updaterName] || Ext.emptyFn;
                newUpdater = function() {
                    updater.apply(this, arguments);
                    scope[callback].apply(scope, arguments);
                };
                newUpdater.$name = updaterName;
                newUpdater.$owner = self;

                this[updaterName] = newUpdater;
            }
        },

        
        destroy: function() {
            this.destroy = Ext.emptyFn;
        }
    });

    
    Base.prototype.callOverridden = Base.prototype.callParent;

    Ext.Base = Base;

}(Ext.Function.flexSetter));






(function() {
    var ExtClass,
        Base = Ext.Base,
        baseStaticMembers = [],
        baseStaticMember, baseStaticMemberLength;

    for (baseStaticMember in Base) {
        if (Base.hasOwnProperty(baseStaticMember)) {
            baseStaticMembers.push(baseStaticMember);
        }
    }

    baseStaticMemberLength = baseStaticMembers.length;

    
    function makeCtor (className) {
        function constructor () {
            
            
            return this.constructor.apply(this, arguments) || null;
        }
        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, {
        
        onBeforeCreated: function(Class, data, hooks) {
        
            Class.addMembers(data);

            hooks.onCreated.call(Class, Class);
            
        },

        
        create: function(Class, data) {
            var name, i;

            if (!Class) {
                Class = makeCtor(
                );
            }

            for (i = 0; i < baseStaticMemberLength; 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;

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

        configNameCache: {},

        getConfigNameMap: function(name) {
            var cache = this.configNameCache,
                map = cache[name],
                capitalizedName;

            if (!map) {
                capitalizedName = name.charAt(0).toUpperCase() + name.substr(1);

                map = cache[name] = {
                    internal: name,
                    initialized: '_is' + capitalizedName + 'Initialized',
                    apply: 'apply' + capitalizedName,
                    update: 'update' + capitalizedName,
                    'set': 'set' + capitalizedName,
                    'get': 'get' + capitalizedName,
                    doSet : 'doSet' + capitalizedName,
                    changeEvent: name.toLowerCase() + 'change'
                };
            }

            return map;
        }
    });

    
    ExtClass.registerPreprocessor('extend', function(Class, data, hooks) {
        
        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('statics', function(Class, data) {
        
        Class.addStatics(data.statics);

        delete data.statics;
    });

    
    ExtClass.registerPreprocessor('inheritableStatics', function(Class, data) {
        
        Class.addInheritableStatics(data.inheritableStatics);

        delete data.inheritableStatics;
    });

    
    ExtClass.registerPreprocessor('config', function(Class, data) {
        
        var config = data.config,
            prototype = Class.prototype;

        delete data.config;

        Ext.Object.each(config, function(name, value) {
            var nameMap = ExtClass.getConfigNameMap(name),
                internalName = nameMap.internal,
                initializedName = nameMap.initialized,
                applyName = nameMap.apply,
                updateName = nameMap.update,
                setName = nameMap.set,
                getName = nameMap.get,
                hasOwnSetter = (setName in prototype) || data.hasOwnProperty(setName),
                hasOwnApplier = (applyName in prototype) || data.hasOwnProperty(applyName),
                hasOwnUpdater = (updateName in prototype) || data.hasOwnProperty(updateName),
                optimizedGetter, customGetter;

            if (value === null || (!hasOwnSetter && !hasOwnApplier && !hasOwnUpdater)) {
                prototype[internalName] = value;
                prototype[initializedName] = true;
            }
            else {
                prototype[initializedName] = false;
            }

            if (!hasOwnSetter) {
                data[setName] = function(value) {
                    var oldValue = this[internalName],
                        applier = this[applyName],
                        updater = this[updateName];

                    if (!this[initializedName]) {
                        this[initializedName] = true;
                    }

                    if (applier) {
                        value = applier.call(this, value, oldValue);
                    }

                    if (typeof value != 'undefined') {
                        this[internalName] = value;

                        if (updater && value !== oldValue) {
                            updater.call(this, value, oldValue);
                        }
                    }

                    return this;
                };
            }

            if (!(getName in prototype) || data.hasOwnProperty(getName)) {
                customGetter = data[getName] || false;

                if (customGetter) {
                    optimizedGetter = function() {
                        return customGetter.apply(this, arguments);
                    };
                }
                else {
                    optimizedGetter = function() {
                        return this[internalName];
                    };
                }

                data[getName] = function() {
                    var currentGetter;

                    if (!this[initializedName]) {
                        this[initializedName] = true;
                        this[setName](this.config[name]);
                    }

                    currentGetter = this[getName];

                    if ('$previous' in currentGetter) {
                        currentGetter.$previous = optimizedGetter;
                    }
                    else {
                        this[getName] = optimizedGetter;
                    }

                    return optimizedGetter.apply(this, arguments);
                };
            }
        });

        Class.addConfig(config, true);
    });

    
    ExtClass.registerPreprocessor('mixins', function(Class, data, hooks) {
        
        var mixins = data.mixins,
            name, mixin, i, ln;

        delete data.mixins;

        Ext.Function.interceptBefore(hooks, 'onCreated', function() {
        
            if (mixins instanceof Array) {
                for (i = 0,ln = mixins.length; i < ln; i++) {
                    mixin = mixins[i];
                    name = mixin.prototype.mixinId || mixin.$className;

                    Class.mixin(name, mixin);
                }
            }
            else {
                for (var mixinName in mixins) {
                    if (mixins.hasOwnProperty(mixinName)) {
                        Class.mixin(mixinName, mixins[mixinName]);
                    }
                }
            }
        });
    });

    
    Ext.extend = function(Class, Parent, members) {
            
        if (arguments.length === 2 && Ext.isObject(Parent)) {
            members = Parent;
            Parent = Class;
            Class = null;
        }

        var cls;

        if (!Parent) {
            throw new Error("[Ext.extend] Attempting to extend from a class which has not been loaded on the page.");
        }

        members.extend = Parent;
        members.preprocessors = [
            'extend'
            ,'statics'
            ,'inheritableStatics'
            ,'mixins'
            ,'config'
        ];

        if (Class) {
            cls = new ExtClass(Class, members);
            
            cls.prototype.constructor = Class;
        } else {
            cls = new ExtClass(members);
        }

        cls.prototype.override = function(o) {
            for (var m in o) {
                if (o.hasOwnProperty(m)) {
                    this[m] = o[m];
                }
            }
        };

        return cls;
    };
}());






(function(Class, alias, arraySlice, arrayFrom, global) {

    
    function makeCtor () {
        function constructor () {
            
            
            return this.constructor.apply(this, arguments) || null;
        }
        return constructor;
    }

    var Manager = Ext.ClassManager = {

        
        classes: {},

        
        existCache: {},

        
        namespaceRewrites: [{
            from: 'Ext.',
            to: Ext
        }],

        
        maps: {
            alternateToName: {},
            aliasToName: {},
            nameToAliases: {},
            nameToAlternates: {}
        },

        
        enableNamespaceParseCache: true,

        
        namespaceParseCache: {},

        
        instantiators: [],

        
        isCreated: function(className) {
            var existCache = this.existCache,
                i, ln, part, root, parts;


            if (this.classes[className] || existCache[className]) {
                return true;
            }

            root = global;
            parts = this.parseNamespace(className);

            for (i = 0, ln = parts.length; i < ln; i++) {
                part = parts[i];

                if (typeof part != 'string') {
                    root = part;
                } else {
                    if (!root || !root[part]) {
                        return false;
                    }

                    root = root[part];
                }
            }

            existCache[className] = true;

            this.triggerCreated(className);

            return true;
        },

        
        createdListeners: [],

        
        nameCreatedListeners: {},

        
        triggerCreated: function(className) {
            var listeners = this.createdListeners,
                nameListeners = this.nameCreatedListeners,
                alternateNames = this.maps.nameToAlternates[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);
            }

            if (alternateNames) {
                names.push.apply(names, alternateNames);
            }

            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];
                }
            }
        },

        
        onCreated: function(fn, scope, className) {
            
            var listeners = this.createdListeners,
                nameListeners = this.nameCreatedListeners,
                listener = {
                    fn: fn,
                    scope: scope
                };

            if (className) {
                if (this.isCreated(className)) {
                    fn.call(scope, className);
                    return;
                }

                if (!nameListeners[className]) {
                    nameListeners[className] = [];
                }

                nameListeners[className].push(listener);
            }
            else {
                listeners.push(listener);
            }
        },

        
        parseNamespace: function(namespace) {

            var cache = this.namespaceParseCache,
                parts,
                rewrites,
                root,
                name,
                rewrite, from, to, i, ln;

            if (this.enableNamespaceParseCache) {
                if (cache.hasOwnProperty(namespace)) {
                    return cache[namespace];
                }
            }

            parts = [];
            rewrites = this.namespaceRewrites;
            root = global;
            name = namespace;

            for (i = 0, ln = rewrites.length; i < ln; i++) {
                rewrite = rewrites[i];
                from = rewrite.from;
                to = rewrite.to;

                if (name === from || name.substring(0, from.length) === from) {
                    name = name.substring(from.length);

                    if (typeof to != 'string') {
                        root = to;
                    } else {
                        parts = parts.concat(to.split('.'));
                    }

                    break;
                }
            }

            parts.push(root);

            parts = parts.concat(name.split('.'));

            if (this.enableNamespaceParseCache) {
                cache[namespace] = parts;
            }

            return parts;
        },

        
        setNamespace: function(name, value) {
            var root = global,
                parts = this.parseNamespace(name),
                ln = parts.length - 1,
                leaf = parts[ln],
                i, part;

            for (i = 0; i < ln; i++) {
                part = parts[i];

                if (typeof part != 'string') {
                    root = part;
                } else {
                    if (!root[part]) {
                        root[part] = {};
                    }

                    root = root[part];
                }
            }

            root[leaf] = value;

            return root[leaf];
        },

        
        createNamespaces: function() {
            var root = global,
                parts, part, i, j, ln, subLn;

            for (i = 0, ln = arguments.length; i < ln; i++) {
                parts = this.parseNamespace(arguments[i]);

                for (j = 0, subLn = parts.length; j < subLn; j++) {
                    part = parts[j];

                    if (typeof part != 'string') {
                        root = part;
                    } else {
                        if (!root[part]) {
                            root[part] = {};
                        }

                        root = root[part];
                    }
                }
            }

            return root;
        },

        
        set: function(name, value) {
            var me = this,
                maps = me.maps,
                nameToAlternates = maps.nameToAlternates,
                targetName = me.getName(value),
                alternates;

            me.classes[name] = me.setNamespace(name, value);

            if (targetName && targetName !== name) {
                maps.alternateToName[name] = targetName;
                alternates = nameToAlternates[targetName] || (nameToAlternates[targetName] = []);
                alternates.push(name);
            }

            return this;
        },

        
        get: function(name) {
            var classes = this.classes,
                root,
                parts,
                part, i, ln;

            if (classes[name]) {
                return classes[name];
            }

            root = global;
            parts = this.parseNamespace(name);

            for (i = 0, ln = parts.length; i < ln; i++) {
                part = parts[i];

                if (typeof part != 'string') {
                    root = part;
                } else {
                    if (!root || !root[part]) {
                        return null;
                    }

                    root = root[part];
                }
            }

            return root;
        },

        
        setAlias: function(cls, alias) {
            var aliasToNameMap = this.maps.aliasToName,
                nameToAliasesMap = this.maps.nameToAliases,
                className;

            if (typeof cls == 'string') {
                className = cls;
            } else {
                className = this.getName(cls);
            }

            if (alias && aliasToNameMap[alias] !== className) {

                aliasToNameMap[alias] = className;
            }

            if (!nameToAliasesMap[className]) {
                nameToAliasesMap[className] = [];
            }

            if (alias) {
                Ext.Array.include(nameToAliasesMap[className], alias);
            }

            return this;
        },

        
        addNameAliasMappings: function(aliases){
            var aliasToNameMap = this.maps.aliasToName,
                nameToAliasesMap = this.maps.nameToAliases,
                className, aliasList, alias, i;

            for (className in aliases) {
                aliasList = nameToAliasesMap[className] ||
                    (nameToAliasesMap[className] = []);

                for (i = 0; i < aliases[className].length; i++) {
                    alias = aliases[className][i];
                    if (!aliasToNameMap[alias]) {
                        aliasToNameMap[alias] = className;
                        aliasList.push(alias);
                    }
                }

            }
            return this;
        },

        
        addNameAlternateMappings: function(alternates) {
            var alternateToName = this.maps.alternateToName,
                nameToAlternates = this.maps.nameToAlternates,
                className, aliasList, alternate, i;

            for (className in alternates) {
                aliasList = nameToAlternates[className] ||
                    (nameToAlternates[className] = []);

                for (i  = 0; i < alternates[className].length; i++) {
                    alternate = alternates[className][i];
                    if (!alternateToName[alternate]) {
                        alternateToName[alternate] = className;
                        aliasList.push(alternate);
                    }
                }

            }
            return this;
        },

        
        getByAlias: function(alias) {
            return this.get(this.getNameByAlias(alias));
        },

        
        getNameByAlias: function(alias) {
            return this.maps.aliasToName[alias] || '';
        },

        
        getNameByAlternate: function(alternate) {
            return this.maps.alternateToName[alternate] || '';
        },

        
        getAliasesByName: function(name) {
            return this.maps.nameToAliases[name] || [];
        },

        
        getName: function(object) {
            return object && object.$className || '';
        },

        
        getClass: function(object) {
            return object && object.self || null;
        },

        
        create: function(className, data, createdFn) {

            var ctor = makeCtor();
            if (typeof data == 'function') {
                data = data(ctor);
            }


            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) {
                
                if (className) {
                    me.set(className, cls);
                }

                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,
                compat = data.compatibility,
                classReady = function () {
                    var cls, temp;

                    if (requires) {
                        temp = requires;
                        requires = null; 

                        
                        
                        
                        Ext.Loader.require(temp, classReady);
                    } else {
                        
                        
                        cls = me.get(overriddenClassName);

                        
                        delete data.override;
                        delete data.compatibility;
                        delete data.requires;
                        delete data.uses;

                        Ext.override(cls, data);

                        
                        
                        
                        me.triggerCreated(className);

                        if (uses) {
                            Ext.Loader.addUsedClasses(uses); 
                        }

                        if (createdFn) {
                            createdFn.call(cls); 
                        }
                    }
                };

            me.existCache[className] = true;

            if (!compat || Ext.checkVersion(compat)) {
                
                me.onCreated(classReady, me, overriddenClassName);
            }

            return me;
        },

        
        instantiateByAlias: function() {
            var alias = arguments[0],
                args = arraySlice.call(arguments),
                className = this.getNameByAlias(alias);

            if (!className) {
                className = this.maps.aliasToName[alias];



                Ext.syncRequire(className);
            }

            args[0] = className;

            return this.instantiate.apply(this, args);
        },

        
        instantiate: function() {
            var name = arguments[0],
                nameType = typeof name,
                args = arraySlice.call(arguments, 1),
                alias = name,
                possibleName, cls;

            if (nameType != 'function') {
                if (nameType != 'string' && args.length === 0) {
                    args = [name];
                    name = name.xclass;
                }


                cls = this.get(name);
            }
            else {
                cls = name;
            }

            
            if (!cls) {
                possibleName = this.getNameByAlias(name);

                if (possibleName) {
                    name = possibleName;

                    cls = this.get(name);
                }
            }

            
            if (!cls) {
                possibleName = this.getNameByAlternate(name);

                if (possibleName) {
                    name = possibleName;

                    cls = this.get(name);
                }
            }

            
            if (!cls) {

                Ext.syncRequire(name);

                cls = this.get(name);
            }


            return this.getInstantiator(args.length)(cls, args);
        },

        
        dynInstantiate: function(name, args) {
            args = arrayFrom(args, true);
            args.unshift(name);

            return this.instantiate.apply(this, 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(',') + ')');
            }

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

        
        getNamesByExpression: function(expression) {
            var nameToAliasesMap = this.maps.nameToAliases,
                names = [],
                name, alias, aliases, possibleName, regex, i, ln;


            if (expression.indexOf('*') !== -1) {
                expression = expression.replace(/\*/g, '(.*?)');
                regex = new RegExp('^' + expression + '$');

                for (name in nameToAliasesMap) {
                    if (nameToAliasesMap.hasOwnProperty(name)) {
                        aliases = nameToAliasesMap[name];

                        if (name.search(regex) !== -1) {
                            names.push(name);
                        }
                        else {
                            for (i = 0, ln = aliases.length; i < ln; i++) {
                                alias = aliases[i];

                                if (alias.search(regex) !== -1) {
                                    names.push(name);
                                    break;
                                }
                            }
                        }
                    }
                }

            } else {
                possibleName = this.getNameByAlias(expression);

                if (possibleName) {
                    names.push(possibleName);
                } else {
                    possibleName = this.getNameByAlternate(expression);

                    if (possibleName) {
                        names.push(possibleName);
                    } else {
                        names.push(expression);
                    }
                }
            }

            return names;
        }
    };

    
    Manager.registerPostprocessor('alias', function(name, cls, data) {
        
        var aliases = data.alias,
            i, ln;

        for (i = 0,ln = aliases.length; i < ln; i++) {
            alias = aliases[i];

            this.setAlias(cls, alias);
        }

    }, ['xtype', 'alias']);

    
    Manager.registerPostprocessor('singleton', function(name, cls, data, fn) {
        
        if (data.singleton) {
            fn.call(this, name, new cls(), data);
        }
        else {
            return true;
        }
        return false;
    });

    
    Manager.registerPostprocessor('alternateClassName', function(name, cls, data) {
        
        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];


            this.set(alternate, cls);
        }
    });

    Ext.apply(Ext, {
        
        create: alias(Manager, 'instantiate'),

        
        widget: function(name, config) {
            
            
            
            
            
            
            
            var xtype = name,
                alias, className, T, load;

            if (typeof xtype != 'string') { 
                
                config = name; 
                xtype = config.xtype;
            } else {
                config = config || {};
            }

            if (config.isComponent) {
                return config;
            }

            alias = 'widget.' + xtype;
            className = Manager.getNameByAlias(alias);

            
            if (!className) {
                load = true;
            }

            T = Manager.get(className);
            if (load || !T) {
                return Manager.instantiateByAlias(alias, config);
            }
            return new T(config);
        },

        
        createByAlias: alias(Manager, 'instantiateByAlias'),

        
        define: function (className, data, createdFn) {
            
            if (data.override) {
                return Manager.createOverride.apply(Manager, arguments);
            }

            return Manager.create.apply(Manager, arguments);
        },

        
        undefine: function(className) {
        
            var classes = Manager.classes,
                maps = Manager.maps,
                aliasToName = maps.aliasToName,
                nameToAliases = maps.nameToAliases,
                alternateToName = maps.alternateToName,
                nameToAlternates = maps.nameToAlternates,
                aliases = nameToAliases[className],
                alternates = nameToAlternates[className],
                parts, partCount, namespace, i;

            delete Manager.namespaceParseCache[className];
            delete nameToAliases[className];
            delete nameToAlternates[className];
            delete classes[className];

            if (aliases) {
                for (i = aliases.length; i--;) {
                    delete aliasToName[aliases[i]];
                }
            }

            if (alternates) {
                for (i = alternates.length; i--; ) {
                    delete alternateToName[alternates[i]];
                }
            }

            parts  = Manager.parseNamespace(className);
            partCount = parts.length - 1;
            namespace = parts[0];

            for (i = 1; i < partCount; i++) {
                namespace = namespace[parts[i]];
                if (!namespace) {
                    return;
                }
            }

            
            try {
                delete namespace[parts[partCount]];
            }
            catch (e) {
                namespace[parts[partCount]] = undefined;
            }
        },

        
        getClassName: alias(Manager, 'getName'),

        
        getDisplayName: function(object) {
            if (object) {
                if (object.displayName) {
                    return object.displayName;
                }

                if (object.$name && object.$class) {
                    return Ext.getClassName(object.$class) + '#' + object.$name;
                }

                if (object.$className) {
                    return object.$className;
                }
            }

            return 'Anonymous';
        },

        
        getClass: alias(Manager, 'getClass'),

        
        namespace: alias(Manager, 'createNamespaces')
    });

    
    Ext.createWidget = Ext.widget;

    
    Ext.ns = Ext.namespace;

    Class.registerPreprocessor('className', function(cls, data) {
        if (data.$className) {
            cls.$className = data.$className;
        }
        
    }, true, 'first');

    Class.registerPreprocessor('alias', function(cls, data) {
        
        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 (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() {
        
            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];


            Ext.Array.include(aliases, widgetPrefix + xtype);
        }

        data.alias = aliases;

    }, ['xtype', 'alias']);

}(Ext.Class, Ext.Function.alias, Array.prototype.slice, Ext.Array.from, Ext.global));



if (Ext._alternatesMetadata) {
   Ext.ClassManager.addNameAlternateMappings(Ext._alternatesMetadata);
   Ext._alternatesMetadata = null;
}

if (Ext._aliasMetadata) {
    Ext.ClassManager.addNameAliasMappings(Ext._aliasMetadata);
    Ext._aliasMetadata = null;
}







Ext.Loader = new function() {
    var Loader = this,
        Manager = Ext.ClassManager,
        Class = Ext.Class,
        flexSetter = Ext.Function.flexSetter,
        alias = Ext.Function.alias,
        pass = Ext.Function.pass,
        defer = Ext.Function.defer,
        arrayErase = Ext.Array.erase,
        dependencyProperties = ['extend', 'mixins', 'requires'],
        isInHistory = {},
        history = [],
        slashDotSlashRe = /\/\.\//g,
        dotRe = /\./g,
        setPathCount = 0;

    Ext.apply(Loader, {

        
        isInHistory: isInHistory,

        
        history: history,

        
        config: {
            
            enabled: false,

            
            scriptChainDelay : false,

            
            disableCaching: true,

            
            disableCachingParam: '_dc',

            
            garbageCollect : false,

            
            paths: {
                'Ext': '.'
            },

            
            preserveScripts : true,

            
            scriptCharset : undefined
        },

        
        setConfig: function(name, value) {
            if (Ext.isObject(name) && arguments.length === 1) {
                Ext.merge(Loader.config, name);

                if ('paths' in name) {
                    Ext.app.collectNamespaces(name.paths);
                }
            }
            else {
                Loader.config[name] = (Ext.isObject(value)) ? Ext.merge(Loader.config[name], value) : value;

                if (name === 'paths') {
                    Ext.app.collectNamespaces(value);
                }
            }

            return Loader;
        },

        
        getConfig: function(name) {
            if (name) {
                return Loader.config[name];
            }

            return Loader.config;
        },

        
        setPath: flexSetter(function(name, path) {
            Loader.config.paths[name] = path;
            Ext.app.namespaces[name] = true;
            setPathCount++;

            return Loader;
        }),

        
        addClassPathMappings: function(paths) {
            var name;

            if(setPathCount == 0){
                Loader.config.paths = paths;
            } else {
                for(name in paths){
                    Loader.config.paths[name] = paths[name];
                }
            }
            setPathCount++;
            return Loader;
        },

        
        getPath: function(className) {
            var path = '',
                paths = Loader.config.paths,
                prefix = Loader.getPrefix(className);

            if (prefix.length > 0) {
                if (prefix === className) {
                    return paths[prefix];
                }

                path = paths[prefix];
                className = className.substring(prefix.length + 1);
            }

            if (path.length > 0) {
                path += '/';
            }

            return path.replace(slashDotSlashRe, '/') + className.replace(dotRe, "/") + '.js';
        },

        
        getPrefix: function(className) {
            var paths = Loader.config.paths,
                prefix, deepestPrefix = '';

            if (paths.hasOwnProperty(className)) {
                return className;
            }

            for (prefix in paths) {
                if (paths.hasOwnProperty(prefix) && prefix + '.' === className.substring(0, prefix.length + 1)) {
                    if (prefix.length > deepestPrefix.length) {
                        deepestPrefix = prefix;
                    }
                }
            }

            return deepestPrefix;
        },

        
        isAClassNameWithAKnownPrefix: function(className) {
            var prefix = Loader.getPrefix(className);

            
            return prefix !== '' && prefix !== className;
        },

        
        require: function(expressions, fn, scope, excludes) {
            if (fn) {
                fn.call(scope);
            }
        },

        
        syncRequire: function() {},

        
        exclude: function(excludes) {
            return {
                require: function(expressions, fn, scope) {
                    return Loader.require(expressions, fn, scope, excludes);
                },

                syncRequire: function(expressions, fn, scope) {
                    return Loader.syncRequire(expressions, fn, scope, excludes);
                }
            };
        },

        
        onReady: function(fn, scope, withDomReady, options) {
            var oldFn;

            if (withDomReady !== false && Ext.onDocumentReady) {
                oldFn = fn;

                fn = function() {
                    Ext.onDocumentReady(oldFn, scope, options);
                };
            }

            fn.call(scope);
        }
    });

    var queue = [],
        isClassFileLoaded = {},
        isFileLoaded = {},
        classNameToFilePathMap = {},
        scriptElements = {},
        readyListeners = [],
        usedClasses = [],
        requiresMap = {},
        comparePriority = function(listenerA, listenerB) {
            return listenerB.priority - listenerA.priority;
        };

    Ext.apply(Loader, {
        
        documentHead: typeof document != 'undefined' && (document.head || document.getElementsByTagName('head')[0]),

        
        isLoading: false,

        
        queue: queue,

        
        isClassFileLoaded: isClassFileLoaded,

        
        isFileLoaded: isFileLoaded,

        
        readyListeners: readyListeners,

        
        optionalRequires: usedClasses,

        
        requiresMap: requiresMap,

        
        numPendingFiles: 0,

        
        numLoadedFiles: 0,

        
        hasFileLoadError: false,

        
        classNameToFilePathMap: classNameToFilePathMap,

        
        scriptsLoading: 0,

        
        syncModeEnabled: false,

        scriptElements: scriptElements,

        
        refreshQueue: function() {
            var ln = queue.length,
                i, item, j, requires;

            

            if (!ln && !Loader.scriptsLoading) {
                return Loader.triggerReady();
            }

            for (i = 0; i < ln; i++) {
                item = queue[i];

                if (item) {
                    requires = item.requires;

                    
                    
                    if (requires.length > Loader.numLoadedFiles) {
                        continue;
                    }

                    
                    for (j = 0; j < requires.length; ) {
                        if (Manager.isCreated(requires[j])) {
                            
                            arrayErase(requires, j, 1);
                        }
                        else {
                            j++;
                        }
                    }

                    
                    if (item.requires.length === 0) {
                        arrayErase(queue, i, 1);
                        item.callback.call(item.scope);
                        Loader.refreshQueue();
                        break;
                    }
                }
            }

            return Loader;
        },

        
        injectScriptElement: function(url, onLoad, onError, scope, charset) {
            var script = document.createElement('script'),
                dispatched = false,
                config = Loader.config,
                onLoadFn = function() {

                    if(!dispatched) {
                        dispatched = true;
                        script.onload = script.onreadystatechange = script.onerror = null;
                        if (typeof config.scriptChainDelay == 'number') {
                            
                            defer(onLoad, config.scriptChainDelay, scope);
                        } else {
                            onLoad.call(scope);
                        }
                        Loader.cleanupScriptElement(script, config.preserveScripts === false, config.garbageCollect);
                    }

                },
                onErrorFn = function(arg) {
                    defer(onError, 1, scope);   
                    Loader.cleanupScriptElement(script, config.preserveScripts === false, config.garbageCollect);
                };

            script.type = 'text/javascript';
            script.onerror = onErrorFn;
            charset = charset || config.scriptCharset;
            if (charset) {
                script.charset = charset;
            }

            
            if ('addEventListener' in script ) {
                script.onload = onLoadFn;
            } else if ('readyState' in script) {   
                script.onreadystatechange = function() {
                    if ( this.readyState == 'loaded' || this.readyState == 'complete' ) {
                        onLoadFn();
                    }
                };
            } else {
                 script.onload = onLoadFn;
            }

            script.src = url;
            (Loader.documentHead || document.getElementsByTagName('head')[0]).appendChild(script);

            return script;
        },

        
        removeScriptElement: function(url) {
            if (scriptElements[url]) {
                Loader.cleanupScriptElement(scriptElements[url], true, !!Loader.getConfig('garbageCollect'));
                delete scriptElements[url];
            }

            return Loader;
        },

        
        cleanupScriptElement: function(script, remove, collect) {
            var prop;
            script.onload = script.onreadystatechange = script.onerror = null;
            if (remove) {
                Ext.removeNode(script);       
                if (collect) {
                    for (prop in script) {
                        try {
                            if (prop != 'src') {
                                
                                
                                script[prop] = null;
                            }
                            delete script[prop];      
                        } catch (cleanEx) {
                            
                        }
                    }
                }
            }

            return Loader;
        },

        
        loadScript: function (options) {
            var config = Loader.getConfig(),
                isString = typeof options == 'string',
                url = isString ? options : options.url,
                onError = !isString && options.onError,
                onLoad = !isString && options.onLoad,
                scope = !isString && options.scope,
                onScriptError = function() {
                    Loader.numPendingFiles--;
                    Loader.scriptsLoading--;

                    if (onError) {
                        onError.call(scope, "Failed loading '" + url + "', please verify that the file exists");
                    }

                    if (Loader.numPendingFiles + Loader.scriptsLoading === 0) {
                        Loader.refreshQueue();
                    }
                },
                onScriptLoad = function () {
                    Loader.numPendingFiles--;
                    Loader.scriptsLoading--;

                    if (onLoad) {
                        onLoad.call(scope);
                    }

                    if (Loader.numPendingFiles + Loader.scriptsLoading === 0) {
                        Loader.refreshQueue();
                    }
                },
                src;

            Loader.isLoading = true;
            Loader.numPendingFiles++;
            Loader.scriptsLoading++;

            src = config.disableCaching ?
                (url + '?' + config.disableCachingParam + '=' + Ext.Date.now()) : url;

            scriptElements[url] = Loader.injectScriptElement(src, onScriptLoad, onScriptError);
        },

        
        loadScriptFile: function(url, onLoad, onError, scope, synchronous) {
            if (isFileLoaded[url]) {
                return Loader;
            }

            var config = Loader.getConfig(),
                noCacheUrl = url + (config.disableCaching ? ('?' + config.disableCachingParam + '=' + Ext.Date.now()) : ''),
                isCrossOriginRestricted = false,
                xhr, status, onScriptError,
                debugSourceURL = "";

            scope = scope || Loader;

            Loader.isLoading = true;

            if (!synchronous) {
                onScriptError = function() {
                };

                scriptElements[url] = Loader.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
            } else {
                if (typeof XMLHttpRequest != 'undefined') {
                    xhr = new XMLHttpRequest();
                } else {
                    xhr = new ActiveXObject('Microsoft.XMLHTTP');
                }

                try {
                    xhr.open('GET', noCacheUrl, false);
                    xhr.send(null);
                } catch (e) {
                    isCrossOriginRestricted = true;
                }

                status = (xhr.status === 1223) ? 204 :
                    (xhr.status === 0 && ((self.location || {}).protocol == 'file:' || (self.location || {}).protocol == 'ionp:')) ? 200 : xhr.status;

                isCrossOriginRestricted = isCrossOriginRestricted || (status === 0);

                if (isCrossOriginRestricted
                ) {
                }
                else if ((status >= 200 && status < 300) || (status === 304)
                ) {
                    
                    
                    if (!Ext.isIE) {
                        debugSourceURL = "\n//@ sourceURL=" + url;
                    }

                    Ext.globalEval(xhr.responseText + debugSourceURL);

                    onLoad.call(scope);
                }
                else {
                }

                
                xhr = null;
            }
        },

        
        syncRequire: function() {
            var syncModeEnabled = Loader.syncModeEnabled;

            if (!syncModeEnabled) {
                Loader.syncModeEnabled = true;
            }

            Loader.require.apply(Loader, arguments);

            if (!syncModeEnabled) {
                Loader.syncModeEnabled = false;
            }

            Loader.refreshQueue();
        },

        
        require: function(expressions, fn, scope, excludes) {
            var excluded = {},
                included = {},
                excludedClassNames = [],
                possibleClassNames = [],
                classNames = [],
                references = [],
                callback,
                syncModeEnabled,
                filePath, expression, exclude, className,
                possibleClassName, i, j, ln, subLn;

            if (excludes) {
                
                excludes = (typeof excludes === 'string') ? [ excludes ] : excludes;

                for (i = 0,ln = excludes.length; i < ln; i++) {
                    exclude = excludes[i];

                    if (typeof exclude == 'string' && exclude.length > 0) {
                        excludedClassNames = Manager.getNamesByExpression(exclude);

                        for (j = 0,subLn = excludedClassNames.length; j < subLn; j++) {
                            excluded[excludedClassNames[j]] = true;
                        }
                    }
                }
            }

            
            expressions = (typeof expressions === 'string') ? [ expressions ] : (expressions ? expressions : []);

            if (fn) {
                if (fn.length > 0) {
                    callback = function() {
                        var classes = [],
                            i, ln;

                        for (i = 0,ln = references.length; i < ln; i++) {
                            classes.push(Manager.get(references[i]));
                        }

                        return fn.apply(this, classes);
                    };
                }
                else {
                    callback = fn;
                }
            }
            else {
                callback = Ext.emptyFn;
            }

            scope = scope || Ext.global;

            for (i = 0,ln = expressions.length; i < ln; i++) {
                expression = expressions[i];

                if (typeof expression == 'string' && expression.length > 0) {
                    possibleClassNames = Manager.getNamesByExpression(expression);
                    subLn = possibleClassNames.length;

                    for (j = 0; j < subLn; j++) {
                        possibleClassName = possibleClassNames[j];

                        if (excluded[possibleClassName] !== true) {
                            references.push(possibleClassName);

                            if (!Manager.isCreated(possibleClassName) && !included[possibleClassName]) {
                                included[possibleClassName] = true;
                                classNames.push(possibleClassName);
                            }
                        }
                    }
                }
            }

            
            
            if (classNames.length > 0) {
                if (!Loader.config.enabled) {
                    throw new Error("Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " +
                             "Missing required class" + ((classNames.length > 1) ? "es" : "") + ": " + classNames.join(', '));
                }
            }
            else {
                callback.call(scope);
                return Loader;
            }

            syncModeEnabled = Loader.syncModeEnabled;

            if (!syncModeEnabled) {
                queue.push({
                    requires: classNames.slice(), 
                                                  
                    callback: callback,
                    scope: scope
                });
            }

            ln = classNames.length;

            for (i = 0; i < ln; i++) {
                className = classNames[i];

                filePath = Loader.getPath(className);

                
                
                
                if (syncModeEnabled && isClassFileLoaded.hasOwnProperty(className)) {
                    if (!isClassFileLoaded[className]) {
                        Loader.numPendingFiles--;
                        Loader.removeScriptElement(filePath);
                        delete isClassFileLoaded[className];
                    }
                }

                if (!isClassFileLoaded.hasOwnProperty(className)) {
                    isClassFileLoaded[className] = false;
                    classNameToFilePathMap[className] = filePath;

                    Loader.numPendingFiles++;
                    Loader.loadScriptFile(
                        filePath,
                        pass(Loader.onFileLoaded, [className, filePath], Loader),
                        pass(Loader.onFileLoadError, [className, filePath], Loader),
                        Loader,
                        syncModeEnabled
                    );
                }
            }

            if (syncModeEnabled) {
                callback.call(scope);

                if (ln === 1) {
                    return Manager.get(className);
                }
            }

            return Loader;
        },

        
        onFileLoaded: function(className, filePath) {
            var loaded = isClassFileLoaded[className];
            Loader.numLoadedFiles++;

            isClassFileLoaded[className] = true;
            isFileLoaded[filePath] = true;

            
            
            if (!loaded) {
                Loader.numPendingFiles--;
            }

            if (Loader.numPendingFiles === 0) {
                Loader.refreshQueue();
            }

        },

        
        onFileLoadError: function(className, filePath, errorMessage, isSynchronous) {
            Loader.numPendingFiles--;
            Loader.hasFileLoadError = true;

        },

        
        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) {
                Loader.isLoading = false;

                if (refClasses.length !== 0) {
                    
                    refClasses = refClasses.slice();
                    usedClasses.length = 0;
                    
                    
                    Loader.require(refClasses, Loader.triggerReady, Loader);
                    return Loader;
                }
            }

            Ext.Array.sort(readyListeners, comparePriority);

            
            
            
            while (readyListeners.length && !Loader.isLoading) {
                
                
                listener = readyListeners.shift();
                listener.fn.call(listener.scope);
            }

            return Loader;
        },

        
        onReady: function(fn, scope, withDomReady, options) {
            var oldFn;

            if (withDomReady !== false && Ext.onDocumentReady) {
                oldFn = fn;

                fn = function() {
                    Ext.onDocumentReady(oldFn, scope, options);
                };
            }

            if (!Loader.isLoading) {
                fn.call(scope);
            }
            else {
                readyListeners.push({
                    fn: fn,
                    scope: scope,
                    priority: (options && options.priority) || 0
                });
            }
        },

        
        historyPush: function(className) {
            if (className && isClassFileLoaded.hasOwnProperty(className) && !isInHistory[className]) {
                isInHistory[className] = true;
                history.push(className);
            }
            return Loader;
        }
    });

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


    
    Ext.require = alias(Loader, 'require');

    
    Ext.syncRequire = alias(Loader, 'syncRequire');

    
    Ext.exclude = alias(Loader, 'exclude');

    
    Ext.onReady = function(fn, scope, options) {
        Loader.onReady(fn, scope, true, options);
    };

    
    Class.registerPreprocessor('loader', function(cls, data, hooks, continueFn) {
        
        var me = this,
            dependencies = [],
            dependency,
            className = Manager.getName(cls),
            i, j, ln, subLn, value, propertyName, propertyValue,
            requiredMap, requiredDep;

        

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


        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) {
        
        var uses = data.uses;
        if (uses) {
            Loader.addUsedClasses(uses);
        }
    });

    Manager.onCreated(Loader.historyPush);
};



if (Ext._classPathMetadata) {
    Ext.Loader.addClassPathMappings(Ext._classPathMetadata);
    Ext._classPathMetadata = null;
}


(function() {
    var scripts = document.getElementsByTagName('script'),
        currentScript = scripts[scripts.length - 1],
        src = currentScript.src,
        path = src.substring(0, src.lastIndexOf('/') + 1),
        Loader = Ext.Loader;


    Loader.setConfig({
        enabled: true,
        disableCaching:
            true,
        paths: {
            'Ext': path + 'src'
        }
    });
})();



Ext._endTime = new Date().getTime();
if (Ext._beforereadyhandler){
    Ext._beforereadyhandler();
}






Ext.Error = Ext.extend(Error, {
    statics: {
        
        ignore: false,

        
        

        
        raise: function(err){
            err = err || {};
            if (Ext.isString(err)) {
                err = { msg: err };
            }

            var method = this.raise.caller,
                msg;

            if (method) {
                if (method.$name) {
                    err.sourceMethod = method.$name;
                }
                if (method.$owner) {
                    err.sourceClass = method.$owner.$className;
                }
            }

            if (Ext.Error.handle(err) !== true) {
                msg = Ext.Error.prototype.toString.call(err);

                Ext.log({
                    msg: msg,
                    level: 'error',
                    dump: err,
                    stack: true
                });

                throw new Ext.Error(err);
            }
        },

        
        handle: function(){
            return Ext.Error.ignore;
        }
    },

    
    name: 'Ext.Error',

    
    constructor: function(config){
        if (Ext.isString(config)) {
            config = { msg: config };
        }

        var me = this;

        Ext.apply(me, config);

        me.message = me.message || me.msg; 
        
    },

    
    toString: function(){
        var me = this,
            className = me.sourceClass ? me.sourceClass : '',
            methodName = me.sourceMethod ? '.' + me.sourceMethod + '(): ' : '',
            msg = me.msg || '(No description provided)';

        return className + methodName + msg;
    }
});


Ext.deprecated = function (suggestion) {
    return Ext.emptyFn;
};








Ext.JSON = (new(function() {
    var me = this,
    encodingFunction,
    decodingFunction,
    useNative = null,
    useHasOwn = !! {}.hasOwnProperty,
    isNative = function() {
        if (useNative === null) {
            useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
        }
        return useNative;
    },
    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 Ext.JSON.encodeDate(o);
        } else if (Ext.isString(o)) {
            return Ext.JSON.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',
        '"': '\\"',
        "\\": '\\\\',
        '\x0b': '\\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);
        }) + '"';
    },


    encodeArray = function(o, newline) {

        var a = ["[", ""], 
            len = o.length,
            i;
        for (i = 0; i < len; i += 1) {
            a.push(Ext.JSON.encodeValue(o[i]), ',');
        }
        
        a[a.length - 1] = ']';
        return a.join("");
    },

    encodeObject = function(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(Ext.JSON.encodeValue(i), ":", Ext.JSON.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 (!encodingFunction) {
            
            encodingFunction = isNative() ? JSON.stringify : me.encodeValue;
        }
        return encodingFunction(o);
    };

    
    me.decode = function(json, safe) {
        if (!decodingFunction) {
            
            decodingFunction = isNative() ? JSON.parse : doDecode;
        }
        try {
            return decodingFunction(json);
        } catch (e) {
            if (safe === true) {
                return null;
            }
            Ext.Error.raise({
                sourceClass: "Ext.JSON",
                sourceMethod: "decode",
                msg: "You're trying to decode an invalid JSON String: " + json
            });
        }
    };
})());

Ext.encode = Ext.JSON.encode;

Ext.decode = Ext.JSON.decode;






Ext.apply(Ext, {
    userAgent: navigator.userAgent.toLowerCase(),
    cache: {},
    idSeed: 1000,
    windowId: 'ext-window',
    documentId: 'ext-document',

    
    isReady: false,

    
    enableGarbageCollector: true,

    
    enableListenerCollection: true,

    
    rootHierarchyState: {},

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


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

                dom: dom,

                
                skipGarbageCollection: !!(dom.getElementById || dom.navigator)
            });

        if (el) {
            el.$cache = entry;
            
            
            entry.el = el;
        }

        return entry;
    },

    updateCacheEntry: function(cacheItem, dom){
        cacheItem.dom = dom;
        if (cacheItem.el) {
            cacheItem.el.dom = dom;
        }
        return cacheItem;
    },

    
    id: function(el, prefix) {
        var me = this,
            sandboxPrefix = '';
        el = Ext.getDom(el, true) || {};
        if (el === document) {
            el.id = me.documentId;
        }
        else if (el === window) {
            el.id = me.windowId;
        }
        if (!el.id) {
            if (me.isSandboxed) {
                sandboxPrefix = Ext.sandboxName.toLowerCase() + '-';
            }
            el.id = sandboxPrefix + (prefix || "ext-gen") + (++Ext.idSeed);
        }
        return el.id;
    },

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

        return function(id) {
            return validIdRe.test(id)
                ? id
                
                
                : id.replace(escapeRx, escapeFn)
                    .replace(leadingNumRx, numEscapeFn);
        };
    }()),

    
    getBody: (function() {
        var body;
        return function() {
            return body || (body = Ext.get(document.body));
        };
    }()),

    
    getHead: (function() {
        var head;
        return function() {
            return head || (head = Ext.get(document.getElementsByTagName("head")[0]));
        };
    }()),

    
    getDoc: (function() {
        var doc;
        return function() {
            return doc || (doc = Ext.get(document));
        };
    }()),

    
    getOrientation: function() {
        return window.innerHeight > window.innerWidth ? 'portrait' : 'landscape';
    },

    
    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 (arg.isStore) {
                    arg.destroyStore();
                } else if (Ext.isFunction(arg.destroy)) {
                    arg.destroy();
                } else if (arg.dom) {
                    arg.remove();
                }
            }
        }
    },

    
    callback: function (callback, scope, args, delay) {
        var fn, ret;

        if (Ext.isFunction(callback)){
            fn = callback;
        } else if (scope && Ext.isString(callback)) {
            fn = scope[callback];
        }

        if (fn) {
            args = args || [];
            scope = scope || window;
            if (delay) {
                Ext.defer(fn, delay, scope, args);
            } else {
                ret = fn.apply(scope, args);
            }
        }

        return ret;
    },

    
    resolveMethod: function(fn, scope) {
        if (Ext.isFunction(fn)) {
            return fn;
        }


        return scope[fn];
    },

    
    htmlEncode : function(value) {
        return Ext.String.htmlEncode(value);
    },

    
    htmlDecode : function(value) {
         return Ext.String.htmlDecode(value);
    },

    
    urlAppend : function(url, s) {
        return Ext.String.urlAppend(url, s);
    }
});


Ext.ns = Ext.namespace;


window.undefined = window.undefined;


(function(){

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



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

    
    Ext.setVersion('ext', '4.2.2.1144');
    Ext.setVersion('extjs', '4.2.2.1144');
    Ext.apply(Ext, {
        
        SSL_SECURE_URL : isSecure && isIE ? 'javascript:\'\'' : 'about:blank',

        

        plainTableCls: Ext.buildSettings.baseCSSPrefix + 'table-plain',

        plainListCls: Ext.buildSettings.baseCSSPrefix + 'list-plain',

        
        enableNestedListenerRemoval : false,

        
        USE_NATIVE_JSON : false,

        
        getDom : function(el, strict) {
            if (!el || !document) {
                return null;
            }
            if (el.dom) {
                return el.dom;
            } else {
                if (typeof el == 'string') {
                    var e = Ext.getElementById(el);
                    
                    
                    if (e && isIE && strict) {
                        if (el == e.getAttribute('id')) {
                            return e;
                        } else {
                            return null;
                        }
                    }
                    return e;
                } else {
                    return el;
                }
            }
        },

        
        removeNode : isIE6 || isIE7 || isIE8
            ? (function() {
                var d;
                return function(n){
                    if(n && n.tagName.toUpperCase() != 'BODY'){
                        (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);

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

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

                        if (isIE8 && n.parentNode) {
                            n.parentNode.removeChild(n);
                        }
                        d = d || document.createElement('div');
                        d.appendChild(n);
                        d.innerHTML = '';
                    }
                };
            }())
            : function(n) {
                if (n && n.parentNode && n.tagName.toUpperCase() != 'BODY') {
                    (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);

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

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

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

        isStrict: isStrict,

        
        isIEQuirks: isIE && (!isStrict && (isIE6 || isIE7 || isIE8 || isIE9)),

        
        isOpera : isOpera,

        
        isOpera10_5 : isOpera10_5,

        
        isWebKit : isWebKit,

        
        isChrome : isChrome,

        
        isSafari : isSafari,

        
        isSafari3 : isSafari3,

        
        isSafari4 : isSafari4,

        
        isSafari5 : isSafari5,

        
        isSafari5_0 : isSafari5_0,


        
        isSafari2 : isSafari2,

        
        isIE : isIE,

        
        isIE6 : isIE6,

        
        isIE7 : isIE7,

        
        isIE7m : isIE6 || isIE7,

        
        isIE7p : isIE && !isIE6,

        
        isIE8 : isIE8,

        
        isIE8m : isIE6 || isIE7 || isIE8,

        
        isIE8p : isIE && !(isIE6 || isIE7),

        
        isIE9 : isIE9,

        
        isIE9m : isIE6 || isIE7 || isIE8 || isIE9,

        
        isIE9p : isIE && !(isIE6 || isIE7 || isIE8),

        
        isIE10 : isIE10,

        
        isIE10m : isIE6 || isIE7 || isIE8 || isIE9 || isIE10,

        
        isIE10p : isIE && !(isIE6 || isIE7 || isIE8 || isIE9),
        
        
        isIE11: isIE11, 
        
        
        isIE11m : isIE6 || isIE7 || isIE8 || isIE9 || isIE10 || isIE11,
 
        
        isIE11p : isIE && !(isIE6 || isIE7 || isIE8 || isIE9 || isIE10),

        
        isGecko : isGecko,

        
        isGecko3 : isGecko3,

        
        isGecko4 : isGecko4,

        
        isGecko5 : isGecko5,

        
        isGecko10 : isGecko10,

        
        isFF3_0 : isFF3_0,

        
        isFF3_5 : isFF3_5,

        
        isFF3_6 : isFF3_6,

        
        isFF4 : 4 <= firefoxVersion && firefoxVersion < 5,

        
        isFF5 : 5 <= firefoxVersion && firefoxVersion < 6,

        
        isFF10 : 10 <= firefoxVersion && firefoxVersion < 11,

        
        isLinux : isLinux,

        
        isWindows : isWindows,

        
        isMac : isMac,

        
        chromeVersion: chromeVersion,

        
        firefoxVersion: firefoxVersion,

        
        ieVersion: ieVersion,

        
        operaVersion: operaVersion,

        
        safariVersion: safariVersion,

        
        webKitVersion: webKitVersion,

        
        isSecure: isSecure,

        
        BLANK_IMAGE_URL : (isIE6 || isIE7) ? '/' + '/www.sencha.com/s.gif' : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',

        
        value : function(v, defaultValue, allowBlank){
            return Ext.isEmpty(v, allowBlank) ? defaultValue : v;
        },

        
        escapeRe : function(s) {
            return s.replace(/([-.*+?\^${}()|\[\]\/\\])/g, "\\$1");
        },

        
        addBehaviors : function(o){
            if(!Ext.isReady){
                Ext.onReady(function(){
                    Ext.addBehaviors(o);
                });
            } else {
                var cache = {}, 
                    parts,
                    b,
                    s;
                for (b in o) {
                    if ((parts = b.split('@'))[1]) { 
                        s = parts[0];
                        if(!cache[s]){
                            cache[s] = Ext.select(s);
                        }
                        cache[s].on(parts[1], o[b]);
                    }
                }
                cache = null;
            }
        },

        
        getScrollbarSize: function (force) {
            if (!Ext.isReady) {
                return {};
            }

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

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

                db.appendChild(div); 

                
                scrollbarSize = {
                    width: div.offsetWidth - div.clientWidth,
                    height: div.offsetHeight - div.clientHeight
                };

                db.removeChild(div);
            }

            return scrollbarSize;
        },

        
        getScrollBarWidth: function(force){
            var size = Ext.getScrollbarSize(force);
            return size.width + 2; 
        },

        
        copyTo : function(dest, source, names, usePrototypeKeys){
            if(typeof names == 'string'){
                names = names.split(/[,;\s]/);
            }

            var n,
                nLen = names? names.length : 0,
                name;

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

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

            return dest;
        },

        
        destroyMembers : function(o){
            for (var i = 1, a = arguments, len = a.length; i < len; i++) {
                Ext.destroy(o[a[i]]);
                delete o[a[i]];
            }
        },

        
        log :
            nullLog,

        
        partition : function(arr, truth){
            var ret = [[],[]],
                a, v,
                aLen = arr.length;

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

            return ret;
        },

        
        invoke : function(arr, methodName){
            var ret  = [],
                args = Array.prototype.slice.call(arguments, 2),
                a, v,
                aLen = arr.length;

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

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

            return ret;
        },

        
        zip : function(){
            var parts = Ext.partition(arguments, function( val ){ return typeof val != 'function'; }),
                arrs = parts[0],
                fn = parts[1][0],
                len = Ext.max(Ext.pluck(arrs, "length")),
                ret = [],
                i,
                j,
                aLen;

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

        
        toSentence: function(items, connector) {
            var length = items.length,
                head,
                tail;

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

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

        
        setGlyphFontFamily: function(fontFamily) {
            Ext._glyphFontFamily = fontFamily;
        },

        
        useShims: isIE6
    });
}());


Ext.application = function(config) {
    var App, paths, ns,
        
        createApp = function() {
            Ext.onReady(function() {
                Ext.app.Application.instance = new App();
            });
        };

    if (typeof config === "string") {
        Ext.require(config, function() {
            App = Ext.ClassManager.get(config);
            createApp();
        });
    }
    else {
        
        
        Ext.Loader.setPath(config.name, config.appFolder || 'app');

        if (paths = config.paths) {
            for (ns in paths) {
                if (paths.hasOwnProperty(ns)) {
                    Ext.Loader.setPath(ns, paths[ns]);
                }
            }
        }

        config['paths processed'] = true;

        
        Ext.define(config.name + ".$application", Ext.apply({
            extend: 'Ext.app.Application' 
        }, config),
        
        function () {
            App = this;
            createApp();
        });
    }
};






(function() {
    Ext.ns('Ext.util');

    var UtilFormat     = Ext.util.Format = {},
        stripTagsRE    = /<\/?[^>]+>/gi,
        stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
        nl2brRe        = /\r?\n/g,
        allHashes      = /^#+$/,

        
        formatPattern = /[\d,\.#]+/,

        
        formatCleanRe  = /[^\d\.#]/g,

        
        
        I18NFormatCleanRe,

        
        formatFns = {};

    Ext.apply(UtilFormat, {
        
        
        thousandSeparator: ',',
        

        
        
        decimalSeparator: '.',
        

        
        
        currencyPrecision: 2,
        

         
        
        currencySign: '$',
        

        
        
        currencyAtEnd: false,
        

        
        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 UtilFormat.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 : UtilFormat.currencyPrecision;
            format += (decimals > 0 ? '.' : '');
            for (; i < decimals; i++) {
                format += '0';
            }
            v = UtilFormat.number(v, format);
            if ((end || UtilFormat.currencyAtEnd) === true) {
                return Ext.String.format("{0}{1}{2}", negativeSign, v, currencySign || UtilFormat.currencySign);
            } else {
                return Ext.String.format("{0}{1}{2}", negativeSign, currencySign || UtilFormat.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 UtilFormat.date(v, format);
            };
        },

        
        stripTags : function(v) {
            return !v ? v : String(v).replace(stripTagsRE, "");
        },

        
        stripScripts : function(v) {
            return !v ? v : String(v).replace(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;
            }
            return result;
        },

        
        number : function(v, formatString) {
            if (!formatString) {
                return v;
            }
            var formatFn = formatFns[formatString];

            
            
            if (!formatFn) {

                var originalFormatString = formatString,
                    comma = UtilFormat.thousandSeparator,
                    decimalSeparator = UtilFormat.decimalSeparator,
                    precision = 0,
                    trimPart = '',
                    hasComma,
                    splitFormat,
                    extraChars,
                    multiplier,
                    trimTrailingZeroes,
                    code;

                
                
                
                
                if (formatString.substr(formatString.length - 2) == '/i') {
                    if (!I18NFormatCleanRe) {
                        I18NFormatCleanRe = new RegExp('[^\\d\\' + UtilFormat.decimalSeparator + ']','g');
                    }
                    formatString = formatString.substr(0, formatString.length - 2);
                    hasComma = formatString.indexOf(comma) != -1;
                    splitFormat = formatString.replace(I18NFormatCleanRe, '').split(decimalSeparator);
                } else {
                    hasComma = formatString.indexOf(',') != -1;
                    splitFormat = formatString.replace(formatCleanRe, '').split('.');
                }
                extraChars = formatString.replace(formatPattern, '');

                if (splitFormat.length > 2) {
                } else if (splitFormat.length === 2) {
                    precision = splitFormat[1].length;

                    
                    trimTrailingZeroes = allHashes.test(splitFormat[1]);
                    if (trimTrailingZeroes) {
                        
                        trimPart = 'trailingZeroes=new RegExp(Ext.String.escapeRegex(utilFormat.decimalSeparator) + "?0+$")'
                    }
                }
                
                
                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];' +
                    '}';
                }

                if (trimTrailingZeroes) {
                    code[code.length] = 'fnum=fnum.replace(trailingZeroes,"");';
                }

                
                code[code.length] = 'if(neg&&fnum!=="' + (precision ? '0.' + Ext.String.repeat('0', precision) : '0') + '")fnum="-"+fnum;';

                code[code.length] = 'return ';

                
                if (extraChars) {
                    code[code.length] = 'formatString.replace(formatPattern, fnum);';
                } else {
                    code[code.length] = 'fnum;';
                }
                code[code.length] = '};'

                formatFn = formatFns[originalFormatString] = Ext.functionFactory('Ext', code.join(''))(Ext);
            }
            return formatFn(v);
        },

        
        numberRenderer : function(format) {
            return function(v) {
                return UtilFormat.number(v, format);
            };
        },

        
        attributes: function(attributes) {
            if (typeof attributes === 'object') {
                var result = [],
                    name;

                for (name in attributes) {
                    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(nl2brRe, '<br/>');
        },

        
        capitalize: Ext.String.capitalize,

        
        ellipsis: Ext.String.ellipsis,

        
        format: Ext.String.format,

        
        htmlDecode: Ext.String.htmlDecode,

        
        htmlEncode: Ext.String.htmlEncode,

        
        leftPad: Ext.String.leftPad,

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

        
        escapeRegex : function(s) {
            return s.replace(/([\-.*+?\^${}()|\[\]\/\\])/g, "\\$1");
        }
    });
}());





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: 1e99,

    
    onTick: function () {
        var me = this,
            tasks = me.tasks,
            now = Ext.Date.now(),
            nextExpires = 1e99,
            len = tasks.length,
            expires, newTasks, i, task, rt, remove;

        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; 
                    try {
                        rt = task.run.apply(task.scope || task, task.args || [++task.taskRunCount]);
                    } catch (taskError) {
                        try {
                            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 (me.fireIdleEvent !== false) {
            Ext.EventManager.idleEvent.fire();
        }
   },

    
    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 = setTimeout(me.timerFn, timeout);
            me.nextExpires = expires;
        }
    }
},
function () {
    var me = this,
        proto = me.prototype;

    
    proto.destroy = proto.stopAll;

    
    Ext.util.TaskManager = Ext.TaskManager = new me();

    
    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.util.TaskManager', {
    extend:  Ext.util.TaskRunner ,

    alternateClassName: [
        'Ext.TaskManager'
    ],

    singleton: true
});





Ext.define('Ext.perf.Accumulator', (function () {
    var currentFrame = null,
        khrome = Ext.global['chrome'],
        formatTpl,
        
        
        getTimestamp = function () {

            getTimestamp = function () {
                return new Date().getTime();
            };
            
            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) {
                    
                }
            } else if (Date.now) {
                getTimestamp = Date.now;
            }

            Ext.perf.getTimestamp = Ext.perf.Accumulator.getTimestamp = getTimestamp;
            return getTimestamp();
        };

    function adjustSet (set, time) {
        set.sum += time;
        set.min = Math.min(set.min, time);
        set.max = Math.max(set.max, time);
    }

    function leaveFrame (time) {
        var totalTime = time ? time : (getTimestamp() - this.time), 
            me = this, 
            accum = me.accum;

        ++accum.count;
        if (! --accum.depth) {
            adjustSet(accum.total, totalTime);
        }
        adjustSet(accum.pure, totalTime - me.childTime);

        currentFrame = me.parent;
        if (currentFrame) {
            ++currentFrame.accum.childCount;
            currentFrame.childTime += totalTime;
        }
    }

    function makeSet () {
        return {
            min: Number.MAX_VALUE,
            max: 0,
            sum: 0
        };
    }

    function makeTap (me, fn) {
        return function () {
            var frame = me.enter(),
                ret = fn.apply(this, arguments);

            frame.leave();
            return ret;
        };
    }

    function round (x) {
        return Math.round(x * 100) / 100;
    }

    function setToJSON (count, childCount, calibration, set) {
        var data = {
            avg: 0,
            min: set.min,
            max: set.max,
            sum: 0
        };

        if (count) {
            calibration = calibration || 0;
            data.sum = set.sum - childCount * calibration;
            data.avg = data.sum / count;
            
            
        }

        return data;
    }

    return {
        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.AbstractComponent': 'render'
                },

































                layout: {
                    'Ext.layout.Context': 'run'
                }
            };
        }

        this.currentConfig = config;

        var key, prop,
            accum, className, methods;
        for (key in config) {
            if (config.hasOwnProperty(key)) {
                prop = config[key];
                accum = Ext.Perf.get(key);

                for (className in prop) {
                    if (prop.hasOwnProperty(className)) {
                        methods = prop[className];
                        accum.tap(className, methods);
                    }
                }
            }
        }

        this.watchGC();
    }
});






Ext.is = {
    init : function(navigator) {
        var platforms = this.platforms,
            ln = platforms.length,
            i, platform;

        navigator = navigator || window.navigator;

        for (i = 0; i < ln; i++) {
            platform = platforms[i];
            this[platform.identity] = platform.regex.test(navigator[platform.property]);
        }

        
        this.Desktop = this.Mac || this.Windows || (this.Linux && !this.Android);
        
        this.Tablet = this.iPad;
        
        this.Phone = !this.Desktop && !this.Tablet;
        
        this.iOS = this.iPhone || this.iPad || this.iPod;
        
        
        this.Standalone = !!window.navigator.standalone;
    },
    
    
    platforms: [{
        property: 'platform',
        regex: /iPhone/i,
        identity: 'iPhone'
    },
    
    
    {
        property: 'platform',
        regex: /iPod/i,
        identity: 'iPod'
    },
    
    
    {
        property: 'userAgent',
        regex: /iPad/i,
        identity: 'iPad'
    },
    
    
    {
        property: 'userAgent',
        regex: /Blackberry/i,
        identity: 'Blackberry'
    },
    
    
    {
        property: 'userAgent',
        regex: /Android/i,
        identity: 'Android'
    },
    
    
    {
        property: 'platform',
        regex: /Mac/i,
        identity: 'Mac'
    },
    
    
    {
        property: 'platform',
        regex: /Win/i,
        identity: 'Windows'
    },
    
    
    {
        property: 'platform',
        regex: /Linux/i,
        identity: 'Linux'
    }]
};

Ext.is.init();


(function(){

    
    
    
    var getStyle = function(element, styleName){
        var view = element.ownerDocument.defaultView,
            style = (view ? view.getComputedStyle(element, null) : element.currentStyle) || element.style;
        return style[styleName];
    },
    supportsVectors = {
        'IE6-quirks':  [0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,1,0,0,1,0,0,1,0,1,0,0,0],
        'IE6-strict':  [0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0],
        'IE7-quirks':  [0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,1,0,0,1,0,0,1,0,1,0,0,0],
        'IE7-strict':  [0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,1,0,0,0],
        'IE8-quirks':  [0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,1,0,0,1,0,0,1,0,1,0,0,0],
        'IE8-strict':  [0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,1,1,0,0,1,0,1,0,0,1],
        'IE9-quirks':  [0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,1,0,0,0,1,0,0,1,0,0,1,0,1,0,0,0],
        'IE9-strict':  [0,1,0,0,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,0,0,0,0,1],
        'IE10-quirks': [1,1,0,0,1,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,0,0,1],
        'IE10-strict': [1,1,0,0,1,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,0,0,1]
    };

function getBrowserKey() {
    var browser = Ext.isIE6 ? 'IE6' : Ext.isIE7 ? 'IE7' : Ext.isIE8 ? 'IE8' :
        Ext.isIE9 ? 'IE9': Ext.isIE10 ? 'IE10' : '';

    return browser ? browser + (Ext.isStrict ? '-strict' : '-quirks') : '';
}

Ext.supports = {
    
    init : function() {
        var me = this,
            doc = document,
            toRun = me.toRun || me.tests,
            n = toRun.length,
            div = n && Ext.isReady && doc.createElement('div'),
            notRun = [],
            browserKey = getBrowserKey(),
            test, vector, value;

        if (div) {
            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>'
            ].join('');

            doc.body.appendChild(div);
        }

        vector = supportsVectors[browserKey];
        while (n--) {
            test = toRun[n];
            value = vector && vector[n];
            if (value !== undefined) {
                me[test.identity] = value;
            } else if (div || test.early) {
                me[test.identity] = test.fn.call(me, doc, div);
            } else {
                notRun.push(test);
            }
        }

        if (div) {
            doc.body.removeChild(div);
        }

        me.toRun = notRun;
    },


    
    PointerEvents: 'pointerEvents' in document.documentElement.style,

    
    
    
    LocalStorage: (function() {
        try {
            return 'localStorage' in window && window['localStorage'] !== null;
        } catch (e) {
            return false;
        }
    })(),

    
    CSS3BoxShadow: 'boxShadow' in document.documentElement.style || 'WebkitBoxShadow' in document.documentElement.style || 'MozBoxShadow' in document.documentElement.style,

    
    ClassList: !!document.documentElement.classList,

    
    OrientationChange: ((typeof window.orientation != 'undefined') && ('onorientationchange' in window)),

    
    DeviceMotion: ('ondevicemotion' in window),

    
    
    
    Touch: ('ontouchstart' in window) && (!Ext.is.Desktop),

    
    TimeoutActualLateness: (function(){
        setTimeout(function(){
            Ext.supports.TimeoutActualLateness = arguments.length !== 0;
        }, 0);
    }()),

    tests: [
        
        {
            identity: 'Transitions',
            fn: function(doc, div) {
                var prefix = [
                        'webkit',
                        'Moz',
                        'o',
                        'ms',
                        'khtml'
                    ],
                    TE = 'TransitionEnd',
                    transitionEndName = [
                        prefix[0] + TE,
                        'transitionend', 
                        prefix[2] + TE,
                        prefix[3] + TE,
                        prefix[4] + TE
                    ],
                    ln = prefix.length,
                    i = 0,
                    out = false;

                for (; i < ln; i++) {
                    if (getStyle(div, prefix[i] + "TransitionProperty")) {
                        Ext.supports.CSS3Prefix = prefix[i];
                        Ext.supports.CSS3TransitionEnd = transitionEndName[i];
                        out = true;
                        break;
                    }
                }
                return out;
            }
        },

        
        {
            identity: 'RightMargin',
            fn: function(doc, div) {
                var view = doc.defaultView;
                return !(view && view.getComputedStyle(div.firstChild.firstChild, null).marginRight != '0px');
            }
        },

        
        {
            identity: 'DisplayChangeInputSelectionBug',
            early: true,
            fn: function() {
                var webKitVersion = Ext.webKitVersion;
                
                return 0 < webKitVersion && webKitVersion < 533;
            }
        },

        
        {
            identity: 'DisplayChangeTextAreaSelectionBug',
            early: true,
            fn: function() {
                var webKitVersion = Ext.webKitVersion;

                
                return 0 < webKitVersion && webKitVersion < 534.24;
            }
        },

        
        {
            identity: 'TransparentColor',
            fn: function(doc, div, view) {
                view = doc.defaultView;
                return !(view && view.getComputedStyle(div.lastChild, null).backgroundColor != 'transparent');
            }
        },

        
        {
            identity: 'ComputedStyle',
            fn: function(doc, div, view) {
                view = doc.defaultView;
                return view && view.getComputedStyle;
            }
        },

        
        {
            identity: 'Svg',
            fn: function(doc) {
                return !!doc.createElementNS && !!doc.createElementNS( "http:/" + "/www.w3.org/2000/svg", "svg").createSVGRect;
            }
        },

        
        {
            identity: 'Canvas',
            fn: function(doc) {
                return !!doc.createElement('canvas').getContext;
            }
        },

        
        {
            identity: 'Vml',
            fn: function(doc) {
                var d = doc.createElement("div");
                d.innerHTML = "<!--[if vml]><br/><br/><![endif]-->";
                return (d.childNodes.length == 2);
            }
        },

        
        {
            identity: 'Float',
            fn: function(doc, div) {
                return !!div.lastChild.style.cssFloat;
            }
        },

        
        {
            identity: 'AudioTag',
            fn: function(doc) {
                return !!doc.createElement('audio').canPlayType;
            }
        },

        
        {
            identity: 'History',
            fn: function() {
                var history = window.history;
                return !!(history && history.pushState);
            }
        },

        
        {
            identity: 'CSS3DTransform',
            fn: function() {
                return (typeof WebKitCSSMatrix != 'undefined' && new WebKitCSSMatrix().hasOwnProperty('m41'));
            }
        },

		
        {
            identity: '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;
            }
        },

        
        {
            identity: 'CSS3BorderRadius',
            fn: function(doc, div) {
                var domPrefixes = ['borderRadius', 'BorderRadius', 'MozBorderRadius', 'WebkitBorderRadius', 'OBorderRadius', 'KhtmlBorderRadius'],
                    pass = false,
                    i;
                for (i = 0; i < domPrefixes.length; i++) {
                    if (document.body.style[domPrefixes[i]] !== undefined) {
                        return true;
                    }
                }
                return pass;
            }
        },

        
        {
            identity: 'GeoLocation',
            fn: function() {
                
                return (typeof navigator != 'undefined' && 'geolocation' in navigator) || (typeof google != 'undefined' && typeof google.gears != 'undefined');
            }
        },
        
        {
            identity: 'MouseEnterLeave',
            fn: function(doc, div){
                return ('onmouseenter' in div && 'onmouseleave' in div);
            }
        },
        
        {
            identity: 'MouseWheel',
            fn: function(doc, div) {
                return ('onmousewheel' in div);
            }
        },
        
        {
            identity: 'Opacity',
            fn: function(doc, div){
                
                if (Ext.isIE6 || Ext.isIE7 || Ext.isIE8) {
                    return false;
                }
                div.firstChild.style.cssText = 'opacity:0.73';
                return div.firstChild.style.opacity == '0.73';
            }
        },
        
        {
            identity: 'Placeholder',
            fn: function(doc) {
                return 'placeholder' in doc.createElement('input');
            }
        },

        
        {
            identity: 'Direct2DBug',
            fn: function() {
                return Ext.isString(document.body.style.msTransformOrigin) && Ext.isIE10m;
            }
        },
        
        {
            identity: 'BoundingClientRect',
            fn: function(doc, div) {
                return Ext.isFunction(div.getBoundingClientRect);
            }
        },
        
        {
            identity: 'RotatedBoundingClientRect',
            fn: function() {
                var body = document.body,
                    supports = false,
                    el = document.createElement('div'),
                    style = el.style;

                if (el.getBoundingClientRect) {
                    style.WebkitTransform = style.MozTransform =
                        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;
            }
        },
        {
            identity: 'IncludePaddingInWidthCalculation',
            fn: function(doc, div){
                return div.childNodes[1].firstChild.offsetWidth == 210;
            }
        },
        {
            identity: 'IncludePaddingInHeightCalculation',
            fn: function(doc, div){
                return div.childNodes[1].firstChild.offsetHeight == 210;
            }
        },

        
        {
            identity: 'ArraySort',
            fn: function() {
                var a = [1,2,3,4,5].sort(function(){ return 0; });
                return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
            }
        },
        
        {
            identity: 'Range',
            fn: function() {
                return !!document.createRange;
            }
        },
        
        {
            identity: 'CreateContextualFragment',
            fn: function() {
                var range = Ext.supports.Range ? document.createRange() : false;

                return range && !!range.createContextualFragment;
            }
        },

        
        {
            identity: 'WindowOnError',
            fn: function () {
                
                return Ext.isIE || Ext.isGecko || Ext.webKitVersion >= 534.16; 
            }
        },

        
        {
            identity: 'TextAreaMaxLength',
            fn: function(){
                var el = document.createElement('textarea');
                return ('maxlength' in el);
            }
        },
        
        
        {
            identity: 'GetPositionPercentage',
            fn: function(doc, div){
               return getStyle(div.childNodes[2], 'left') == '10%';
            }
        },
        
        {
            identity: 'PercentageHeightOverflowBug',
            fn: function(doc) {
                var hasBug = false,
                    style, el;

                if (Ext.getScrollbarSize().height) {
                    
                    el = doc.createElement('div');
                    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;
            }
        },
        
        {
            identity: 'xOriginBug',
            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);
            }
        },

        
        {
            identity: 'ScrollWidthInlinePaddingBug',
            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;
            }
        }
    ]
};
}());

Ext.supports.init(); 






Ext.util.DelayedTask = function(fn, scope, args, cancelOnDelay) {
    var me = this,
        delay,
        call = function() {
            clearInterval(me.id);
            me.id = null;
            fn.apply(scope, args || []);
            Ext.EventManager.idleEvent.fire();
        };

    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 = setInterval(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,
      DelayedTask = Ext.util.DelayedTask;

  return {
                                     

    
    isEvent: true,
    
    
    suspended: 0,

    noOptions: {},

    constructor: function(observable, name) {
        this.name = name;
        this.observable = observable;
        this.listeners = [];
    },

    addListener: function(fn, scope, options) {
        var me = this,
            listeners, listener, priority, isNegativePriority, highestNegativePriorityIndex,
            hasNegativePriorityIndex, length, index, i, listenerPriority;

        scope = scope || me.observable;


        if (!me.isListening(fn, scope)) {
            listener = me.createListener(fn, scope, options);
            if (me.firing) {
                
                me.listeners = me.listeners.slice(0);
            }
            listeners = me.listeners;
            index = length = listeners.length;
            priority = options && options.priority;
            highestNegativePriorityIndex = me._highestNegativePriorityIndex;
            hasNegativePriorityIndex = (highestNegativePriorityIndex !== undefined);
            if (priority) {
                
                
                isNegativePriority = (priority < 0);
                if (!isNegativePriority || hasNegativePriorityIndex) {
                    
                    
                    
                    
                    
                    
                    for(i = (isNegativePriority ? highestNegativePriorityIndex : 0); i < length; i++) {
                        
                        listenerPriority = listeners[i].o ? listeners[i].o.priority||0 : 0;
                        if (listenerPriority < priority) {
                            index = i;
                            break;
                        }
                    }
                } else {
                    
                    
                    
                    me._highestNegativePriorityIndex = index;
                }
            } else if (hasNegativePriorityIndex) {
                
                
                
                
                index = highestNegativePriorityIndex;
            }

            if (!isNegativePriority && index <= highestNegativePriorityIndex) {
                me._highestNegativePriorityIndex ++;
            }
            if (index === length) {
                me.listeners[length] = listener;
            } else {
                arrayInsert(me.listeners, index, [listener]);
            }
        }
    },

    createListener: function(fn, scope, o) {
        scope = scope || this.observable;

        var me = this,
            listener = {
                fn: fn,
                scope: scope,
                ev: me
            },
            handler = fn;

        
        
        if (o) {
            listener.o = o;
            if (o.single) {
                handler = me.createSingle(handler, listener, o, scope);
            }
            if (o.target) {
                handler = me.createTargeted(handler, listener, o, scope);
            }
            if (o.delay) {
                handler = me.createDelayed(handler, listener, o, scope);
            }
            if (o.buffer) {
                handler = me.createBuffered(handler, listener, o, scope);
            }
        }

        listener.fireFn = handler;
        return listener;
    },

    findListener: function(fn, scope) {
        var listeners = this.listeners,
        i = listeners.length,
        listener,
        s;

        while (i--) {
            listener = listeners[i];
            if (listener) {
                s = listener.scope;

                
                
                
                if (listener.fn == fn && (s == (scope || this.observable))) {
                    return i;
                }
            }
        }

        return - 1;
    },

    isListening: function(fn, scope) {
        return this.findListener(fn, scope) !== -1;
    },

    removeListener: function(fn, scope) {
        var me = this,
            index,
            listener,
            highestNegativePriorityIndex,
            k;
        index = me.findListener(fn, scope);
        if (index != -1) {
            listener = me.listeners[index];
            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);

            
            
            if (highestNegativePriorityIndex) {
                if (index < highestNegativePriorityIndex) {
                    me._highestNegativePriorityIndex --;
                } else if (index === highestNegativePriorityIndex && index === me.listeners.length) {
                    delete me._highestNegativePriorityIndex;
                }
            }
            return true;
        }

        return false;
    },

    
    clearListeners: function() {
        var listeners = this.listeners,
            i = listeners.length;

        while (i--) {
            this.removeListener(listeners[i].fn, listeners[i].scope);
        }
    },

    suspend: function() {
        this.suspended += 1;
    },

    resume: function() {
        if (this.suspended) {
            this.suspended--;
        }
    },

    fire: function() {
        var me = this,
            listeners = me.listeners,
            count = listeners.length,
            i,
            args,
            listener,
            len;

        if (!me.suspended && count > 0) {
            me.firing = true;
            args = arguments.length ? arraySlice.call(arguments, 0) : []
            len = args.length;
            for (i = 0; i < count; i++) {
                listener = listeners[i];
                if (listener.o) {
                    args[len] = listener.o;
                }
                if (listener && listener.fireFn.apply(listener.scope || me.observable, args) === false) {
                    return (me.firing = false);
                }
            }
        }
        me.firing = false;
        return true;
    },

    createTargeted: function (handler, listener, o, scope) {
        return function(){
            if (o.target === arguments[0]){
                handler.apply(scope, arguments);
            }
        };
    },

    createBuffered: function (handler, listener, o, scope) {
        listener.task = new DelayedTask();
        return function() {
            listener.task.delay(o.buffer, handler, scope, toArray(arguments));
        };
    },

    createDelayed: function (handler, listener, o, scope) {
        return function() {
            var task = new DelayedTask();
            if (!listener.tasks) {
                listener.tasks = [];
            }
            listener.tasks.push(task);
            task.delay(o.delay || 10, handler, scope, toArray(arguments));
        };
    },

    createSingle: function (handler, listener, o, scope) {
        return function() {
            var event = listener.ev;

            if (event.removeListener(listener.fn, scope) && event.observable) {
                
                
                event.observable.hasListeners[event.name]--;
            }

            return handler.apply(scope, arguments);
        };
    }
  };
});






Ext.EventManager = new function() {
    var EventManager = this,
        doc = document,
        win = window,
        escapeRx = /\\/g,
        prefix = Ext.baseCSSPrefix,
        
        supportsAddEventListener = !Ext.isIE9 && 'addEventListener' in doc,
        readyEvent,
        initExtCss = function() {
            
            var bd = doc.body || doc.getElementsByTagName('body')[0],
                cls = [],
                htmlCls = [],
                supportsLG = Ext.supports.CSS3LinearGradient,
                supportsBR = Ext.supports.CSS3BorderRadius,
                html;

            
            if (!Ext.scopeCss) {
                cls.push(prefix + 'body');
            }

            if (!bd) {
                return false;
            }

            html = bd.parentNode;

            function add (c) {
                cls.push(prefix + c);
            }

            
            if (Ext.isIE && Ext.isIE9m) {
                add('ie');

                
                
                
                
                
                
                
                
                
                
                
                if (Ext.isIE6) {
                    add('ie6');
                } else { 
                    add('ie7p');

                    if (Ext.isIE7) {
                        add('ie7');
                    } else {
                        add('ie8p');

                        if (Ext.isIE8) {
                            add('ie8');
                        } else {
                            add('ie9p');

                            if (Ext.isIE9) {
                                add('ie9');
                            }
                        }
                    }
                }

                if (Ext.isIE7m) {
                    add('ie7m');
                }
                if (Ext.isIE8m) {
                    add('ie8m');
                }
                if (Ext.isIE9m) {
                    add('ie9m');
                }
                if (Ext.isIE7 || Ext.isIE8) {
                    add('ie78');
                }
            }
            
            if (Ext.isIE10) {
                add('ie10');
            }
            
            if (Ext.isGecko) {
                add('gecko');
                if (Ext.isGecko3) {
                    add('gecko3');
                }
                if (Ext.isGecko4) {
                    add('gecko4');
                }
                if (Ext.isGecko5) {
                    add('gecko5');
                }
            }
            if (Ext.isOpera) {
                add('opera');
            }
            if (Ext.isWebKit) {
                add('webkit');
            }
            if (Ext.isSafari) {
                add('safari');
                if (Ext.isSafari2) {
                    add('safari2');
                }
                if (Ext.isSafari3) {
                    add('safari3');
                }
                if (Ext.isSafari4) {
                    add('safari4');
                }
                if (Ext.isSafari5) {
                    add('safari5');
                }
                if (Ext.isSafari5_0) {
                    add('safari5_0')
                }
            }
            if (Ext.isChrome) {
                add('chrome');
            }
            if (Ext.isMac) {
                add('mac');
            }
            if (Ext.isLinux) {
                add('linux');
            }
            if (!supportsBR) {
                add('nbr');
            }
            if (!supportsLG) {
                add('nlg');
            }

            
            if (html) {
                if (Ext.isStrict && (Ext.isIE6 || Ext.isIE7)) {
                    Ext.isBorderBox = false;
                }
                else {
                    Ext.isBorderBox = true;
                }

                if(!Ext.isBorderBox) {
                    htmlCls.push(prefix + 'content-box');
                }
                if (Ext.isStrict) {
                    htmlCls.push(prefix + 'strict');
                } else {
                    htmlCls.push(prefix + 'quirks');
                }
                Ext.fly(html, '_internal').addCls(htmlCls);
            }

            Ext.fly(bd, '_internal').addCls(cls);
            return true;
        };

    Ext.apply(EventManager, {
        
        hasBoundOnReady: false,

        
        hasFiredReady: false,

        
        deferReadyEvent : 1,

        
        onReadyChain : [],

        
        readyEvent:
            (function () {
                readyEvent = new Ext.util.Event();
                readyEvent.fire = function () {
                    Ext._beforeReadyTime = Ext._beforeReadyTime || new Date().getTime();
                    readyEvent.self.prototype.fire.apply(readyEvent, arguments);
                    Ext._afterReadytime = new Date().getTime();
                };
                return readyEvent;
            }()),

        
        idleEvent: new Ext.util.Event(),

        
        isReadyPaused: function(){
            return (/[?&]ext-pauseReadyFire\b/i.test(location.search) && !Ext._continueFireReady);
        },

        
        bindReadyEvent: function() {
            if (EventManager.hasBoundOnReady) {
                return;
            }

            
            if ( doc.readyState == 'complete'  ) {  
                EventManager.onReadyEvent({
                    type: doc.readyState || 'body'
                });
            } else {
                doc.addEventListener('DOMContentLoaded', EventManager.onReadyEvent, false);
                win.addEventListener('load', EventManager.onReadyEvent, false);
                EventManager.hasBoundOnReady = true;
            }
        },

        onReadyEvent : function(e) {
            if (e && e.type) {
                EventManager.onReadyChain.push(e.type);
            }

            if (EventManager.hasBoundOnReady) {
                doc.removeEventListener('DOMContentLoaded', EventManager.onReadyEvent, false);
                win.removeEventListener('load', EventManager.onReadyEvent, false);
            }

            if (!Ext.isReady) {
                EventManager.fireDocReady();
            }
        },

        
        fireDocReady: function() {
            if (!Ext.isReady) {
                Ext._readyTime = new Date().getTime();
                Ext.isReady = true;

                Ext.supports.init();
                EventManager.onWindowUnload();
                readyEvent.onReadyChain = EventManager.onReadyChain;    

                if (Ext.isNumber(EventManager.deferReadyEvent)) {
                    Ext.Function.defer(EventManager.fireReadyEvent, EventManager.deferReadyEvent);
                    EventManager.hasDocReadyTimer = true;
                } else {
                    EventManager.fireReadyEvent();
                }
            }
        },

        
        fireReadyEvent: function() {

            
            
            EventManager.hasDocReadyTimer = false;
            EventManager.isFiring = true;

            
            
            
            while (readyEvent.listeners.length && !EventManager.isReadyPaused()) {
                readyEvent.fire();
            }
            EventManager.isFiring = false;
            EventManager.hasFiredReady = true;
            Ext.EventManager.idleEvent.fire();
        },

        
        onDocumentReady: function(fn, scope, options) {
            options = options || {};
            
            options.single = true;
            readyEvent.addListener(fn, scope, options);

            
            
            if (!(EventManager.isFiring || EventManager.hasDocReadyTimer)) {
                if (Ext.isReady) {
                    EventManager.fireReadyEvent();
                } else {
                    EventManager.bindReadyEvent();
                }
            }
        },

        

        
        stoppedMouseDownEvent: new Ext.util.Event(),

        
        propRe: /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|freezeEvent)$/,

        
        getId : function(element) {
            var id;

            element = Ext.getDom(element);

            if (element === doc || element === win) {
                id = element === doc ? Ext.documentId : Ext.windowId;
            }
            else {
                id = Ext.id(element);
            }

            if (!Ext.cache[id]) {
                Ext.addCacheEntry(id, null, element);
            }

            return id;
        },

        
        prepareListenerConfig: function(element, config, isRemove) {
            var propRe = EventManager.propRe,
                key, value, args;

            
            for (key in config) {
                if (config.hasOwnProperty(key)) {
                    
                    if (!propRe.test(key)) {
                        value = config[key];
                        
                        
                        if (typeof value == 'function') {
                            
                            args = [element, key, value, config.scope, config];
                        } else {
                            
                            args = [element, key, value.fn, value.scope, value];
                        }

                        if (isRemove) {
                            EventManager.removeListener.apply(EventManager, args);
                        } else {
                            EventManager.addListener.apply(EventManager, args);
                        }
                    }
                }
            }
        },

        mouseEnterLeaveRe: /mouseenter|mouseleave/,

        
        normalizeEvent: function(eventName, fn) {
            if (EventManager.mouseEnterLeaveRe.test(eventName) && !Ext.supports.MouseEnterLeave) {
                if (fn) {
                    fn = Ext.Function.createInterceptor(fn, EventManager.contains);
                }
                eventName = eventName == 'mouseenter' ? 'mouseover' : 'mouseout';
            } else if (eventName == 'mousewheel' && !Ext.supports.MouseWheel && !Ext.isOpera) {
                eventName = 'DOMMouseScroll';
            }
            return {
                eventName: eventName,
                fn: fn
            };
        },

        
        contains: function(event) {
            event = event.browserEvent || event;
            var parent = event.currentTarget,
                child = EventManager.getRelatedTarget(event);

            if (parent && parent.firstChild) {
                while (child) {
                    if (child === parent) {
                        return false;
                    }
                    child = child.parentNode;
                    if (child && (child.nodeType != 1)) {
                        child = null;
                    }
                }
            }
            return true;
        },

        
        addListener: function(element, eventName, fn, scope, options) {
            
            if (typeof eventName !== 'string') {
                EventManager.prepareListenerConfig(element, eventName);
                return;
            }

            var dom = element.dom || Ext.getDom(element),
                hasAddEventListener, bind, wrap, cache, id, cacheItem, capture;
            
            if (typeof fn === 'string') {
                fn = Ext.resolveMethod(fn, scope || element);
            }


            
            options = options || {};

            bind = EventManager.normalizeEvent(eventName, fn);
            wrap = EventManager.createListenerWrap(dom, eventName, bind.fn, scope, options);
            
            
            cache = EventManager.getEventListenerCache(element.dom ? element : dom, eventName);
            eventName = bind.eventName;

            
            hasAddEventListener = supportsAddEventListener || (Ext.isIE9 && !dom.attachEvent);
            
            if (!hasAddEventListener) {
                id = EventManager.normalizeId(dom);
                
                
                if (id) {
                    cacheItem = Ext.cache[id][eventName];
                    if (cacheItem && cacheItem.firing) {
                        
                        
                        
                        
                        
                        
                        cache = EventManager.cloneEventListenerCache(dom, eventName);
                    }
                }
            }

            capture = !!options.capture;
            cache.push({
                fn: fn,
                wrap: wrap,
                scope: scope,
                capture: capture 
            });

            if (!hasAddEventListener) {
                
                
                if (cache.length === 1) {
                    id = EventManager.normalizeId(dom, true);
                    fn = Ext.Function.bind(EventManager.handleSingleEvent, EventManager, [id, eventName], true);
                    Ext.cache[id][eventName] = {
                        firing: false,
                        fn: fn
                    };
                    dom.attachEvent('on' + eventName, fn);
                }
            } else {
                dom.addEventListener(eventName, wrap, capture);
            }

            if (dom == doc && eventName == 'mousedown') {
                EventManager.stoppedMouseDownEvent.addListener(wrap);
            }
        },
        
        
        
        normalizeId: function(dom, force) {
            var id;
            if (dom === document) {
                id = Ext.documentId;
            } else if (dom === window) {
                id = Ext.windowId;
            } else {
                id = dom.id;
            }
            if (!id && force) {
                id = EventManager.getId(dom);
            }
            return id;
        },
        
        handleSingleEvent: function(e, id, eventName) {
            
            
            
            var listenerCache = EventManager.getEventListenerCache(id, eventName),
                attachItem = Ext.cache[id][eventName],
                len, i;
                
            
            
            
            if (attachItem.firing) {
                return;
            }
                
            attachItem.firing = true;
            for (i = 0, len = listenerCache.length; i < len; ++i) {
                listenerCache[i].wrap(e);
            }
            attachItem.firing = false;
            
        },

        
        removeListener : function(element, eventName, fn, scope) {
            
            if (typeof eventName !== 'string') {
                EventManager.prepareListenerConfig(element, eventName, true);
                return;
            }

            var dom = Ext.getDom(element),
                id, el = element.dom ? element : Ext.get(dom),
                cache = EventManager.getEventListenerCache(el, eventName),
                bindName = EventManager.normalizeEvent(eventName).eventName,
                i = cache.length, j, cacheItem, hasRemoveEventListener,
                listener, wrap;
                
            if (!dom) {
                return;
            }

            
            hasRemoveEventListener = supportsAddEventListener || (Ext.isIE9 && !dom.detachEvent);
            
            if (typeof fn === 'string') {
                fn = Ext.resolveMethod(fn, scope || element);
            }

            while (i--) {
                listener = cache[i];

                if (listener && (!fn || listener.fn == fn) && (!scope || listener.scope === scope)) {
                    wrap = listener.wrap;

                    
                    if (wrap.task) {
                        clearTimeout(wrap.task);
                        delete wrap.task;
                    }

                    
                    j = wrap.tasks && wrap.tasks.length;
                    if (j) {
                        while (j--) {
                            clearTimeout(wrap.tasks[j]);
                        }
                        delete wrap.tasks;
                    }

                    if (!hasRemoveEventListener) {
                        
                        
                        id = EventManager.normalizeId(dom, true);
                        cacheItem = Ext.cache[id][bindName];
                        if (cacheItem && cacheItem.firing) {
                            
                            cache = EventManager.cloneEventListenerCache(dom, bindName);
                        }
                        
                        if (cache.length === 1) {
                            fn = cacheItem.fn;
                            delete Ext.cache[id][bindName];
                            dom.detachEvent('on' + bindName, fn);
                        }
                    } else {
                        dom.removeEventListener(bindName, wrap, listener.capture);
                    }

                    if (wrap && dom == doc && eventName == 'mousedown') {
                        EventManager.stoppedMouseDownEvent.removeListener(wrap);
                    }

                    
                    Ext.Array.erase(cache, i, 1);
                }
            }
        },

        
        removeAll : function(element) {
            var id = (typeof element === 'string') ? element : element.id,
                cache, events, eventName;

            
            if (id && (cache = Ext.cache[id])) {
                events = cache.events;
    
                for (eventName in events) {
                    if (events.hasOwnProperty(eventName)) {
                        EventManager.removeListener(element, eventName);
                    }
                }
                cache.events = {};
             }
        },

        
        purgeElement : function(element, eventName) {
            var dom = Ext.getDom(element),
                i = 0, len, childNodes;

            if (eventName) {
                EventManager.removeListener(element, eventName);
            } else {
                EventManager.removeAll(element);
            }

            if (dom && dom.childNodes) {
                childNodes = dom.childNodes;
                for (len = childNodes.length; i < len; i++) {
                    EventManager.purgeElement(childNodes[i], eventName);
                }
            }
        },

        
        createListenerWrap : function(dom, ename, fn, scope, options) {
            options = options || {};

            var gen, wrap = function(e, args) {
                var f;
                
                if (!gen) {
                    f = ['if(!' + Ext.name + ') {return;}'];

                    if (options.buffer || options.delay || options.freezeEvent) {
                        if (options.freezeEvent) {
                            
                            
                            f.push('e = X.EventObject.setEvent(e);');
                        }
                        f.push('e = new X.EventObjectImpl(e, ' + (options.freezeEvent ? 'true' : 'false' ) + ');');
                    } else {
                        f.push('e = X.EventObject.setEvent(e);');
                    }

                    if (options.delegate) {
                        
                        
                        f.push('var result, t = e.getTarget("' + (options.delegate + '').replace(escapeRx, '\\\\') + '", this);');
                        f.push('if(!t) {return;}');
                    } else {
                        f.push('var t = e.target, result;');
                    }

                    if (options.target) {
                        f.push('if(e.target !== options.target) {return;}');
                    }

                    if (options.stopEvent) {
                        f.push('e.stopEvent();');
                    } else {
                        if(options.preventDefault) {
                            f.push('e.preventDefault();');
                        }
                        if(options.stopPropagation) {
                            f.push('e.stopPropagation();');
                        }
                    }

                    if (options.normalized === false) {
                        f.push('e = e.browserEvent;');
                    }

                    if (options.buffer) {
                        f.push('(wrap.task && clearTimeout(wrap.task));');
                        f.push('wrap.task = setTimeout(function() {');
                    }

                    if (options.delay) {
                        f.push('wrap.tasks = wrap.tasks || [];');
                        f.push('wrap.tasks.push(setTimeout(function() {');
                    }

                    
                    f.push('result = fn.call(scope || dom, e, t, options);');

                    if (options.single) {
                        f.push('evtMgr.removeListener(dom, ename, fn, scope);');
                    }

                    
                    
                    if (ename !== 'mousemove' && ename !== 'unload') {
                        f.push('if (evtMgr.idleEvent.listeners.length) {');
                        f.push('evtMgr.idleEvent.fire();');
                        f.push('}');
                    }

                    if (options.delay) {
                        f.push('}, ' + options.delay + '));');
                    }

                    if (options.buffer) {
                        f.push('}, ' + options.buffer + ');');
                    }
                    f.push('return result;');

                    gen = Ext.cacheableFunctionFactory('e', 'options', 'fn', 'scope', 'ename', 'dom', 'wrap', 'args', 'X', 'evtMgr', f.join('\n'));
                }

                return gen.call(dom, e, options, fn, scope, ename, dom, wrap, args, Ext, EventManager);
            };
            return wrap;
        },
        
        
        getEventCache: function(element) {
            var elementCache, eventCache, id;
            
            if (!element) {
                return [];
            }

            if (element.$cache) {
                elementCache = element.$cache;
            } else {
                
                if (typeof element === 'string') {
                    id = element;
                } else {
                    id = EventManager.getId(element);
                }
                elementCache = Ext.cache[id];
            }
            eventCache = elementCache.events || (elementCache.events = {});
            return eventCache;
        },

        
        getEventListenerCache : function(element, eventName) {
            var eventCache = EventManager.getEventCache(element);
            return eventCache[eventName] || (eventCache[eventName] = []);
        },
        
        
        cloneEventListenerCache: function(element, eventName){
            var eventCache = EventManager.getEventCache(element),
                out;
                
            if (eventCache[eventName]) {
                out = eventCache[eventName].slice(0);
            } else {
                out = [];
            }
            eventCache[eventName] = out;
            return out;
        },

        
        mouseLeaveRe: /(mouseout|mouseleave)/,
        mouseEnterRe: /(mouseover|mouseenter)/,

        
        stopEvent: function(event) {
            EventManager.stopPropagation(event);
            EventManager.preventDefault(event);
        },

        
        stopPropagation: function(event) {
            event = event.browserEvent || event;
            if (event.stopPropagation) {
                event.stopPropagation();
            } else {
                event.cancelBubble = true;
            }
        },

        
        preventDefault: function(event) {
            event = event.browserEvent || event;
            if (event.preventDefault) {
                event.preventDefault();
            } else {
                event.returnValue = false;
                
                try {
                  
                  if (event.ctrlKey || event.keyCode > 111 && event.keyCode < 124) {
                      event.keyCode = -1;
                  }
                } catch (e) {
                    
                }
            }
        },

        
        getRelatedTarget: function(event) {
            event = event.browserEvent || event;
            var target = event.relatedTarget;
            if (!target) {
                if (EventManager.mouseLeaveRe.test(event.type)) {
                    target = event.toElement;
                } else if (EventManager.mouseEnterRe.test(event.type)) {
                    target = event.fromElement;
                }
            }
            return EventManager.resolveTextNode(target);
        },

        
        getPageX: function(event) {
            return EventManager.getPageXY(event)[0];
        },

        
        getPageY: function(event) {
            return EventManager.getPageXY(event)[1];
        },

        
        getPageXY: function(event) {
            event = event.browserEvent || event;
            var x = event.pageX,
                y = event.pageY,
                docEl = doc.documentElement,
                body = doc.body;

            
            if (!x && x !== 0) {
                x = event.clientX + (docEl && docEl.scrollLeft || body && body.scrollLeft || 0) - (docEl && docEl.clientLeft || body && body.clientLeft || 0);
                y = event.clientY + (docEl && docEl.scrollTop  || body && body.scrollTop  || 0) - (docEl && docEl.clientTop  || body && body.clientTop  || 0);
            }
            return [x, y];
        },

        
        getTarget: function(event) {
            event = event.browserEvent || event;
            return EventManager.resolveTextNode(event.target || event.srcElement);
        },

        
        
        
        
        resolveTextNode: Ext.isGecko ?
            function(node) {
                if (node) {
                    
                    var s = HTMLElement.prototype.toString.call(node);
                    if (s !== '[xpconnect wrapped native prototype]' && s !== '[object XULElement]') {
                        return node.nodeType == 3 ? node.parentNode: node;
                    }
                }
            }
            :
            function(node) {
                return node && node.nodeType == 3 ? node.parentNode: node;
            },

        

        
        curWidth: 0,
        curHeight: 0,

        
        onWindowResize: function(fn, scope, options) {
            var resize = EventManager.resizeEvent;

            if (!resize) {
                EventManager.resizeEvent = resize = new Ext.util.Event();
                EventManager.on(win, 'resize', EventManager.fireResize, null, {buffer: 100});
            }
            resize.addListener(fn, scope, options);
        },

        
        fireResize: function() {
            var w = Ext.Element.getViewWidth(),
                h = Ext.Element.getViewHeight();

             
             if (EventManager.curHeight != h || EventManager.curWidth != w) {
                 EventManager.curHeight = h;
                 EventManager.curWidth = w;
                 EventManager.resizeEvent.fire(w, h);
             }
        },

        
        removeResizeListener: function(fn, scope) {
            var resize = EventManager.resizeEvent;
            if (resize) {
                resize.removeListener(fn, scope);
            }
        },

        
        onWindowUnload: function(fn, scope, options) {
            var unload = EventManager.unloadEvent;

            if (!unload) {
                EventManager.unloadEvent = unload = new Ext.util.Event();
                EventManager.addListener(win, 'unload', EventManager.fireUnload);
            }
            if (fn) {
                unload.addListener(fn, scope, options);
            }
        },

        
        fireUnload: function() {
            
            try {
                
                doc = win = undefined;

                var gridviews, i, ln,
                    el, cache;

                EventManager.unloadEvent.fire();
                
                if (Ext.isGecko3) {
                    gridviews = Ext.ComponentQuery.query('gridview');
                    i = 0;
                    ln = gridviews.length;
                    for (; i < ln; i++) {
                        gridviews[i].scrollToTop();
                    }
                }
                
                cache = Ext.cache;

                for (el in cache) {
                    if (cache.hasOwnProperty(el)) {
                        EventManager.removeAll(el);
                    }
                }
            } catch(e) {
            }
        },

        
        removeUnloadListener: function(fn, scope) {
            var unload = EventManager.unloadEvent;
            if (unload) {
                unload.removeListener(fn, scope);
            }
        },

        
        useKeyDown: Ext.isWebKit ?
                       parseInt(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1], 10) >= 525 :
                       !((Ext.isGecko && !Ext.isWindows) || (Ext.isOpera && Ext.operaVersion < 12)),

        
        getKeyEvent: function() {
            return EventManager.useKeyDown ? 'keydown' : 'keypress';
        }
    });

    
    if(!supportsAddEventListener && document.attachEvent) {
        Ext.apply( EventManager, {
            

            
            pollScroll : function() {
                var scrollable = true;

                try {
                    document.documentElement.doScroll('left');
                } catch(e) {
                    scrollable = false;
                }

                
                if (scrollable && document.body) {
                    EventManager.onReadyEvent({
                        type:'doScroll'
                    });
                } else {
                    
                    EventManager.scrollTimeout = setTimeout(EventManager.pollScroll, 20);
                }

                return scrollable;
            },

            
            scrollTimeout: null,

            
            readyStatesRe  : /complete/i,

            
            checkReadyState: function() {
                var state = document.readyState;

                if (EventManager.readyStatesRe.test(state)) {
                    EventManager.onReadyEvent({
                        type: state
                    });
                }
            },

            bindReadyEvent: function() {
                var topContext = true;

                if (EventManager.hasBoundOnReady) {
                    return;
                }

                
                try {
                    topContext = window.frameElement === undefined;
                } catch(e) {
                    
                    
                    topContext = false;
                }

                if (!topContext || !doc.documentElement.doScroll) {
                    EventManager.pollScroll = Ext.emptyFn;   
                }

                
                if (EventManager.pollScroll() === true) {
                    return;
                }

                
                if (doc.readyState == 'complete' )  {
                    EventManager.onReadyEvent({type: 'already ' + (doc.readyState || 'body') });
                } else {
                    doc.attachEvent('onreadystatechange', EventManager.checkReadyState);
                    window.attachEvent('onload', EventManager.onReadyEvent);
                    EventManager.hasBoundOnReady = true;
                }
            },

            onReadyEvent : function(e) {
                if (e && e.type) {
                    EventManager.onReadyChain.push(e.type);
                }

                if (EventManager.hasBoundOnReady) {
                    document.detachEvent('onreadystatechange', EventManager.checkReadyState);
                    window.detachEvent('onload', EventManager.onReadyEvent);
                }

                if (Ext.isNumber(EventManager.scrollTimeout)) {
                    clearTimeout(EventManager.scrollTimeout);
                    delete EventManager.scrollTimeout;
                }

                if (!Ext.isReady) {
                    EventManager.fireDocReady();
                }
            },

            
            onReadyChain : []
        });
    }


    
    Ext.onReady = function(fn, scope, options) {
        Ext.Loader.onReady(fn, scope, true, options);
    };

    
    Ext.onDocumentReady = EventManager.onDocumentReady;

    
    EventManager.on = EventManager.addListener;

    
    EventManager.un = EventManager.removeListener;

    Ext.onReady(initExtCss);
};



Ext.define('Ext.util.Observable', function(Observable) {

    var emptyFn = Ext.emptyFn,
        emptyArray = [],
        arrayProto = Array.prototype,
        arraySlice = arrayProto.slice,
        ExtEvent = Ext.util.Event,
        
        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.observable[this.managedListeners ? 'mun' : 'un'].apply(this.observable, this.args);
    };

    return {

        

                                                         

        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) {
                
                

                
                

                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;

            Ext.apply(me, config);

            
            if (!me.hasListeners) {
                me.hasListeners = new me.HasListeners();
            }

            me.events = me.events || {};
            if (me.listeners) {
                me.on(me.listeners);
                me.listeners = null; 
            }

            if (me.bubbleEvents) {
                me.enableBubble(me.bubbleEvents);
            }
        },

        onClassExtended: function (T) {
            if (!T.HasListeners) {
                
                
                Observable.prepareClass(T);
            }
        },

        
        
        eventOptionsRe : /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|element|destroyable|vertical|horizontal|freezeEvent|priority)$/,

        
        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 (!me.eventOptionsRe.test(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 (typeof fn === 'string') {
                    scope = scope || me;
                    fn = Ext.resolveMethod(fn, scope);
                }

                if (fn !== emptyFn) {
                    managedListeners.push({
                        item: item,
                        ename: ename,
                        fn: fn,
                        scope: scope,
                        options: options
                    });

                    item.on(ename, fn, scope, options);

                    
                    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 (!me.eventOptionsRe.test(ename)) {
                            me.removeManagedListener(item, ename, config.fn || config, config.scope || options.scope || scope);
                        }
                    }
                }
            } else {
                managedListeners = me.managedListeners ? me.managedListeners.slice() : [];
                
                if (typeof fn === 'string') {
                    scope = scope || me;
                    fn = Ext.resolveMethod(fn, scope);
                }

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

        
        fireEventArgs: function(eventName, args) {
            eventName = eventName.toLowerCase();
            var me = this,
                events = me.events,
                event = events && events[eventName],
                ret = true;

            
            
            if (event && me.hasListeners[eventName]) {
                ret = me.continueFireEvent(eventName, args || emptyArray, event.bubble);
            }
            return ret;
        },

        
        continueFireEvent: function(eventName, args, bubbles) {
            var target = this,
                queue, event,
                ret = true;

            do {
                if (target.eventsSuspended) {
                    if ((queue = target.eventQueue)) {
                        queue.push([eventName, args, bubbles]);
                    }
                    return ret;
                } else {
                    event = 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) {
            var me = this,
                config, event,
                prevListenerCount = 0;

            
            if (typeof ename !== 'string') {
                options = ename;
                for (ename in options) {
                    if (options.hasOwnProperty(ename)) {
                        config = options[ename];
                        if (!me.eventOptionsRe.test(ename)) {
                            
                            me.addListener(ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
                        }
                    }
                }
                if (options && options.destroyable) {
                    return new ListenerRemover(me, options);
                }
            }
            
            else {
                ename = ename.toLowerCase();
                event = me.events[ename];
                if (event && event.isEvent) {
                    prevListenerCount = event.listeners.length;
                } else {
                    me.events[ename] = event = new ExtEvent(me, ename);
                }

                
                if (typeof fn === 'string') {
                    scope = scope || me;
                    fn = Ext.resolveMethod(fn, scope);
                }

                if (fn !== emptyFn) {
                    event.addListener(fn, scope, options);

                    
                    
                    if (event.listeners.length !== prevListenerCount) {
                        me.hasListeners._incr_(ename);
                    }
                    if (options && options.destroyable) {
                        return new ListenerRemover(me, ename, fn, scope, options);
                    }
                }
            }
        },

        
        removeListener: function(ename, fn, scope) {
            var me = this,
                config,
                event,
                options;

            if (typeof ename !== 'string') {
                options = ename;
                for (ename in options) {
                    if (options.hasOwnProperty(ename)) {
                        config = options[ename];
                        if (!me.eventOptionsRe.test(ename)) {
                            me.removeListener(ename, config.fn || config, config.scope || options.scope);
                        }
                    }
                }
            } else {
                ename = ename.toLowerCase();
                event = me.events[ename];
                if (event && event.isEvent) {
                    if (typeof fn === 'string') {
                        scope = scope || me;
                        fn = Ext.resolveMethod(fn, scope);
                    }
                    
                    if (event.removeListener(fn, scope)) {
                        me.hasListeners._decr_(ename);
                    }
                }
            }
        },

        
        clearListeners: function() {
            var events = this.events,
                hasListeners = this.hasListeners,
                event,
                key;

            for (key in events) {
                if (events.hasOwnProperty(key)) {
                    event = events[key];
                    if (event.isEvent) {
                        delete hasListeners[key];
                        event.clearListeners();
                    }
                }
            }

            this.clearManagedListeners();
        },


        
        clearManagedListeners : function() {
            var managedListeners = this.managedListeners || [],
                i = 0,
                len = managedListeners.length;

            for (; i < len; i++) {
                this.removeManagedListenerItem(true, managedListeners[i]);
            }

            this.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.un(managedListener.ename, managedListener.fn, managedListener.scope);
                if (!isClear) {
                    Ext.Array.remove(this.managedListeners, managedListener);
                }
            }
        },


        
        addEvents: function(o) {
            var me = this,
                events = me.events || (me.events = {}),
                arg, args, i;

            if (typeof o == 'string') {
                for (args = arguments, i = args.length; i--; ) {
                    arg = args[i];
                    if (!events[arg]) {
                        events[arg] = true;
                    }
                }
            } else {
                Ext.applyIf(me.events, o);
            }
        },

        
        hasListener: function(ename) {
            return !!this.hasListeners[ename.toLowerCase()];
        },

        
        suspendEvents: function(queueSuspended) {
            this.eventsSuspended += 1;
            if (queueSuspended && !this.eventQueue) {
                this.eventQueue = [];
            }
        },

        
        suspendEvent: function(eventName) {
            var len = arguments.length,
                i, event;

            for (i = 0; i < len; i++) {
                event = this.events[arguments[i]];

                
                if (event && event.suspend) {
                    event.suspend();
                }
            }
        },

        
        resumeEvent: function() {
            var len = arguments.length,
                i, event;

            for (i = 0; i < len; i++) {

                
                event = this.events[arguments[i]];
                if (event && event.resume) {
                    event.resume();
                }
            }
        },

        
        resumeEvents: function() {
            var me = this,
                queued = me.eventQueue,
                qLen, q;

            if (me.eventsSuspended && ! --me.eventsSuspended) {
                delete me.eventQueue;

                if (queued) {
                    qLen = queued.length;
                    for (q = 0; q < qLen; q++) {
                        me.continueFireEvent.apply(me, queued[q]);
                    }
                }
            }
        },

        
        relayEvents : function(origin, events, prefix) {
            var me = this,
                len = events.length,
                i = 0,
                oldName,
                relayers = {};

            for (; i < len; i++) {
                oldName = events[i];

                
                relayers[oldName] = me.createRelayer(prefix ? prefix + oldName : oldName);
            }
            
            
            
            me.mon(origin, relayers, null, null, undefined);

            
            return new ListenerRemover(me, origin, relayers);
        },

        
        createRelayer: function(newName, beginEnd) {
            var me = this;
            return function() {
                return me.fireEventArgs.call(me, newName, beginEnd ? arraySlice.apply(arguments, beginEnd) : arguments);
            };
        },

        
        enableBubble: function(eventNames) {
            if (eventNames) {
                var me = this,
                    names = (typeof eventNames == 'string') ? arguments : eventNames,
                    length = names.length,
                    events = me.events,
                    ename, event, i;

                for (i = 0; i < length; ++i) {
                    ename = names[i].toLowerCase();
                    event = events[ename];

                    if (!event || typeof event == 'boolean') {
                        events[ename] = event = new ExtEvent(me, ename);
                    }

                    
                    
                    me.hasListeners._incr_(ename);

                    event.bubble = true;
                }
            }
        }
    };
}, function() {
    var Observable = this,
        proto = Observable.prototype,
        HasListeners = function () {},
        prepareMixin = function (T) {
            if (!T.HasListeners) {
                var proto = T.prototype;

                
                Observable.prepareClass(T, this);

                
                
                T.onExtended(function (U) {
                    
                    Observable.prepareClass(U);
                });

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

    HasListeners.prototype = {
        
        _decr_: function (ev) {
            if (! --this[ev]) {
                
                
                
                delete this[ev];
            }
        },
        _incr_: function (ev) {
            if (this.hasOwnProperty(ev)) {
                
                ++this[ev];
            } else {
                
                
                this[ev] = 1;
            }
        }
    };

    proto.HasListeners = Observable.HasListeners = HasListeners;

    Observable.createAlias({
        
        on: 'addListener',
        
        un: 'removeListener',
        
        mon: 'addManagedListener',
        
        mun: 'removeManagedListener'
    });

    
    Observable.observeClass = Observable.observe;

    
    Ext.globalEvents = globalEvents = new Observable({
        events: {
            idle: Ext.EventManager.idleEvent,
            ready: Ext.EventManager.readyEvent
        }
    });

    
    Ext.on = function() {
        return globalEvents.addListener.apply(globalEvents, arguments);
    };

    
    Ext.un = function() {
        return globalEvents.removeListener.apply(globalEvents, arguments);
    };

    
    
    
    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.EventObjectImpl', {
                             

    
    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.0) {
                
                
                
                
                
                
                scale = 120;
            } else {
                
                
                scale = 12;
            }

            
            
            
            
            scale *= 3;
        } else {
            
            scale = 120;
        }

        return scale;
    }()),

    
    clickRe: /(dbl)?click/,
    
    safariKeys: {
        3: 13, 
        63234: 37, 
        63235: 39, 
        63232: 38, 
        63233: 40, 
        63276: 33, 
        63277: 34, 
        63272: 46, 
        63273: 36, 
        63275: 35 
    },
    
    btnMap: Ext.isIE ? {
        1: 0,
        4: 1,
        2: 2
    } : {
        0: 0,
        1: 1,
        2: 2
    },
    
    
    
    

    constructor: function(event, freezeEvent){
        if (event) {
            this.setEvent(event.browserEvent || event, freezeEvent);
        }
    },

    setEvent: function(event, freezeEvent){
        var me = this, button;

        if (event === me || (event && event.browserEvent)) { 
            return event;
        }
        me.browserEvent = event;
        if (event) {
            
            button = event.button ? me.btnMap[event.button] : (event.which ? event.which - 1 : -1);
            if (me.clickRe.test(event.type) && button == -1) {
                button = 0;
            }
            me.type = event.type;
            me.button = button;
            me.shiftKey = event.shiftKey;
            
            me.ctrlKey = event.ctrlKey || event.metaKey || false;
            me.altKey = event.altKey;
            
            me.keyCode = event.keyCode;
            me.charCode = event.charCode;
            
            me.target = Ext.EventManager.getTarget(event);
            me.relatedTarget = Ext.EventManager.getRelatedTarget(event);
            me.currentTarget = event.currentTarget;
            me.xy = (freezeEvent ? me.getXY() : null);
        } else {
            me.button = -1;
            me.shiftKey = false;
            me.ctrlKey = false;
            me.altKey = false;
            me.keyCode = 0;
            me.charCode = 0;
            me.target = null;
            me.xy = [0, 0];
        }
        return me;
    },
    
    
    clone: function() {
        return new this.self(this.browserEvent, this);
    },

    
    stopEvent: function(){
        this.stopPropagation();
        this.preventDefault();
    },

    
    preventDefault: function(){
        if (this.browserEvent) {
            Ext.EventManager.preventDefault(this.browserEvent);
        }
    },

    
    stopPropagation: function(){
        var browserEvent = this.browserEvent;

        if (browserEvent) {
            if (browserEvent.type == 'mousedown') {
                Ext.EventManager.stoppedMouseDownEvent.fire(this);
            }
            Ext.EventManager.stopPropagation(browserEvent);
        }
    },

    
    getCharCode: function(){
        return this.charCode || this.keyCode;
    },

    
    getKey: function(){
        return this.normalizeKey(this.keyCode || this.charCode);
    },

    
    normalizeKey: function(key){
        
        return Ext.isWebKit ? (this.safariKeys[key] || key) : key;
    },

    
    getPageX: function(){
        return this.getX();
    },

    
    getPageY: function(){
        return this.getY();
    },

    
    getX: function() {
        return this.getXY()[0];
    },

    
    getY: function() {
        return this.getXY()[1];
    },

    
    getXY: function() {
        if (!this.xy) {
            
            this.xy = Ext.EventManager.getPageXY(this.browserEvent);
        }
        return this.xy;
    },

    
    getTarget : function(selector, maxDepth, returnEl){
        if (selector) {
            return Ext.fly(this.target).findParent(selector, maxDepth, returnEl);
        }
        return returnEl ? Ext.get(this.target) : this.target;
    },

    
    getRelatedTarget : function(selector, maxDepth, returnEl){
        if (selector && this.relatedTarget) {
            return Ext.fly(this.relatedTarget).findParent(selector, maxDepth, returnEl);
        }
        return returnEl ? Ext.get(this.relatedTarget) : this.relatedTarget;
    },

    
    correctWheelDelta : function (delta) {
        var scale = this.WHEEL_SCALE,
            ret = Math.round(delta / scale);

        if (!ret && delta) {
            ret = (delta < 0) ? -1 : 1; 
        }

        return ret;
    },

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

    
    getWheelDelta : function(){
        var deltas = this.getWheelDeltas();

        return deltas.y;
    },

    
    within : function(el, related, allowEl){
        if(el){
            var t = related ? this.getRelatedTarget() : this.getTarget(),
                result;

            if (t) {
                result = Ext.fly(el, '_internal').contains(t);
                if (!result && allowEl) {
                    result = t == Ext.getDom(el);
                }
                return result;
            }
        }
        return false;
    },

    
    isNavKeyPress : function(){
        var me = this,
            k = this.normalizeKey(me.keyCode);

       return (k >= 33 && k <= 40) ||  
       k == me.RETURN ||
       k == me.TAB ||
       k == me.ESC;
    },

    
    isSpecialKey : function(){
        var k = this.normalizeKey(this.keyCode);
        return (this.type == 'keypress' && this.ctrlKey) ||
        this.isNavKeyPress() ||
        (k == this.BACKSPACE) || 
        (k >= 16 && k <= 20) || 
        (k >= 44 && k <= 46);   
    },

    
    getPoint : function(){
        var xy = this.getXY();
        return new Ext.util.Point(xy[0], xy[1]);
    },

   
    hasModifier : function(){
        return this.ctrlKey || this.altKey || this.shiftKey || this.metaKey;
    },

    
    injectEvent: (function () {
        var API,
            dispatchers = {}, 
            crazyIEButtons;

        

        
        

        if (!Ext.isIE9m && document.createEvent) { 
            API = {
                createHtmlEvent: function (doc, type, bubbles, cancelable) {
                    var event = doc.createEvent('HTMLEvents');

                    event.initEvent(type, bubbles, cancelable);
                    return event;
                },

                createMouseEvent: function (doc, type, bubbles, cancelable, detail,
                                            clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
                                            button, relatedTarget) {
                    var event = doc.createEvent('MouseEvents'),
                        view = doc.defaultView || window;

                    if (event.initMouseEvent) {
                        event.initMouseEvent(type, bubbles, cancelable, view, detail,
                                    clientX, clientY, clientX, clientY, ctrlKey, altKey,
                                    shiftKey, metaKey, button, relatedTarget);
                    } else { 
                        event = doc.createEvent('UIEvents');
                        event.initEvent(type, bubbles, cancelable);
                        event.view = view;
                        event.detail = detail;
                        event.screenX = clientX;
                        event.screenY = clientY;
                        event.clientX = clientX;
                        event.clientY = clientY;
                        event.ctrlKey = ctrlKey;
                        event.altKey = altKey;
                        event.metaKey = metaKey;
                        event.shiftKey = shiftKey;
                        event.button = button;
                        event.relatedTarget = relatedTarget;
                    }

                    return event;
                },

                createUIEvent: function (doc, type, bubbles, cancelable, detail) {
                    var event = doc.createEvent('UIEvents'),
                        view = doc.defaultView || window;

                    event.initUIEvent(type, bubbles, cancelable, view, detail);
                    return event;
                },

                fireEvent: function (target, type, event) {
                    target.dispatchEvent(event);
                },

                fixTarget: function (target) {
                    
                    if (target == window && !target.dispatchEvent) {
                        return document;
                    }

                    return target;
                }
            };
        } else if (document.createEventObject) { 
            crazyIEButtons = { 0: 1, 1: 4, 2: 2 };

            API = {
                createHtmlEvent: function (doc, type, bubbles, cancelable) {
                    var event = doc.createEventObject();
                    event.bubbles = bubbles;
                    event.cancelable = cancelable;
                    return event;
                },

                createMouseEvent: function (doc, type, bubbles, cancelable, detail,
                                            clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
                                            button, relatedTarget) {
                    var event = doc.createEventObject();
                    event.bubbles = bubbles;
                    event.cancelable = cancelable;
                    event.detail = detail;
                    event.screenX = clientX;
                    event.screenY = clientY;
                    event.clientX = clientX;
                    event.clientY = clientY;
                    event.ctrlKey = ctrlKey;
                    event.altKey = altKey;
                    event.shiftKey = shiftKey;
                    event.metaKey = metaKey;
                    event.button = crazyIEButtons[button] || button;
                    event.relatedTarget = relatedTarget; 
                    return event;
                },

                createUIEvent: function (doc, type, bubbles, cancelable, detail) {
                    var event = doc.createEventObject();
                    event.bubbles = bubbles;
                    event.cancelable = cancelable;
                    return event;
                },

                fireEvent: function (target, type, event) {
                    target.fireEvent('on' + type, event);
                },

                fixTarget: function (target) {
                    if (target == document) {
                        
                        
                        return document.documentElement;
                    }

                    return target;
                }
            };
        }

        
        

        Ext.Object.each({
                load:   [false, false],
                unload: [false, false],
                select: [true, false],
                change: [true, false],
                submit: [true, true],
                reset:  [true, false],
                resize: [true, false],
                scroll: [true, false]
            },
            function (name, value) {
                var bubbles = value[0], cancelable = value[1];
                dispatchers[name] = function (targetEl, srcEvent) {
                    var e = API.createHtmlEvent(name, bubbles, cancelable);
                    API.fireEvent(targetEl, name, e);
                };
            });

        
        

        function createMouseEventDispatcher (type, detail) {
            var cancelable = (type != 'mousemove');
            return function (targetEl, srcEvent) {
                var xy = srcEvent.getXY(),
                    e = API.createMouseEvent(targetEl.ownerDocument, type, true, cancelable,
                                detail, xy[0], xy[1], srcEvent.ctrlKey, srcEvent.altKey,
                                srcEvent.shiftKey, srcEvent.metaKey, srcEvent.button,
                                srcEvent.relatedTarget);
                API.fireEvent(targetEl, type, e);
            };
        }

        Ext.each(['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mousemove', 'mouseout'],
            function (eventName) {
                dispatchers[eventName] = createMouseEventDispatcher(eventName, 1);
            });

        
        

        Ext.Object.each({
                focusin:  [true, false],
                focusout: [true, false],
                activate: [true, true],
                focus:    [false, false],
                blur:     [false, false]
            },
            function (name, value) {
                var bubbles = value[0], cancelable = value[1];
                dispatchers[name] = function (targetEl, srcEvent) {
                    var e = API.createUIEvent(targetEl.ownerDocument, name, bubbles, cancelable, 1);
                    API.fireEvent(targetEl, name, e);
                };
            });

        
        if (!API) {
            

            dispatchers = {}; 

            API = {
                fixTarget: Ext.identityFn
            };
        }

        function cannotInject (target, srcEvent) {
        }

        return function (target) {
            var me = this,
                dispatcher = dispatchers[me.type] || cannotInject,
                t = target ? (target.dom || target) : me.getTarget();

            t = API.fixTarget(t);
            dispatcher(t, me);
        };
    }()) 

}, function() {

Ext.EventObject = new Ext.EventObjectImpl();

});






Ext.define('Ext.dom.AbstractQuery', {
    
    select: function(q, root) {
        var results = [],
            nodes,
            i,
            j,
            qlen,
            nlen;

        root = root || document;

        if (typeof root == 'string') {
            root = document.getElementById(root);
        }

        q = q.split(",");

        for (i = 0,qlen = q.length; i < qlen; i++) {
            if (typeof q[i] == 'string') {
                
                
                if (typeof q[i][0] == '@') {
                    nodes = root.getAttributeNode(q[i].substring(1));
                    results.push(nodes);
                } else {
                    nodes = root.querySelectorAll(q[i]);

                    for (j = 0,nlen = nodes.length; j < nlen; j++) {
                        results.push(nodes[j]);
                    }
                }
            }
        }

        return results;
    },

    
    selectNode: function(q, root) {
        return this.select(q, root)[0];
    },

    
    is: function(el, q) {
        if (typeof el == "string") {
            el = document.getElementById(el);
        }
        return this.select(q).indexOf(el) !== -1;
    }

});





Ext.define('Ext.dom.AbstractHelper', {
    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,
    styleSepRe: /\s*(?::|;)\s*/,

    
    attributeTransform: { cls : 'class', htmlFor : 'for' },

    closeTags: {},

    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 (!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) {
        if (styles) {
            var i = 0,
                len;

            el = Ext.fly(el, '_applyStyles');
            if (typeof styles == 'function') {
                styles = styles.call();
            }
            if (typeof styles == 'string') {
                styles = Ext.util.Format.trim(styles).split(this.styleSepRe);
                for (len = styles.length; i < len;) {
                    el.setStyle(styles[i++], styles[i++]);
                }
            } else if (Ext.isObject(styles)) {
                el.setStyle(styles);
            }
        }
    },

    
    insertHtml: function(where, el, html) {
        var hash = {},
            setStart,
            range,
            frag,
            rangeEl;

        where = where.toLowerCase();

        
        hash.beforebegin = ['BeforeBegin', 'previousSibling'];
        hash.afterend = ['AfterEnd', 'nextSibling'];

        range = el.ownerDocument.createRange();
        setStart = 'setStart' + (this.endRe.test(where) ? 'After' : 'Before');
        if (hash[where]) {
            range[setStart](el);
            frag = range.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) {
                range[setStart](el[rangeEl]);
                frag = range.createContextualFragment(html);
                if (where == 'afterbegin') {
                    el.insertBefore(frag, el.firstChild);
                }
                else {
                    el.appendChild(frag);
                }
            }
            else {
                el.innerHTML = html;
            }
            return el[rangeEl];
        }

        throw 'Illegal insertion point -> "' + 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', 'nextSibling');
    },

    
    insertFirst: function(el, o, returnElement) {
        return this.doInsert(el, o, returnElement, 'afterbegin', 'firstChild');
    },

    
    append: function(el, o, returnElement) {
        return this.doInsert(el, o, returnElement, 'beforeend', '', true);
    },

    
    overwrite: function(el, o, returnElement) {
        el = Ext.getDom(el);
        el.innerHTML = this.markup(o);
        return returnElement ? Ext.get(el.firstChild) : el.firstChild;
    },

    doInsert: function(el, o, returnElement, pos, sibling, append) {
        var newNode = this.insertHtml(pos, Ext.getDom(el), this.markup(o));
        return returnElement ? Ext.get(newNode, true) : newNode;
    }

});



Ext.define('Ext.dom.AbstractElement_static', {
    override: 'Ext.dom.AbstractElement',

    inheritableStatics: {
        unitRe: /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
        camelRe: /(-[a-z])/gi,
        msRe: /^-ms-/,
        cssRe: /([a-z0-9\-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*)?;?/gi,
        opacityRe: /alpha\(opacity=(.*)\)/i,
        propertyCache: {},
        defaultUnit : "px",
        borders: {l: 'border-left-width', r: 'border-right-width', t: 'border-top-width', b: 'border-bottom-width'},
        paddings: {l: 'padding-left', r: 'padding-right', t: 'padding-top', b: 'padding-bottom'},
        margins: {l: 'margin-left', r: 'margin-right', t: 'margin-top', b: 'margin-bottom'},
        
        addUnits: function(size, units) {
            
            if (typeof size == 'number') {
                return size + (units || this.defaultUnit || 'px');
            }

            
            if (size === "" || size == "auto" || size === undefined || size === null) {
                return size || '';
            }

            
            if (!this.unitRe.test(size)) {
                return size || '';
            }

            return size;
        },

        
        isAncestor: function(p, c) {
            var ret = false;

            p = Ext.getDom(p);
            c = Ext.getDom(c);
            if (p && c) {
                if (p.contains) {
                    return p.contains(c);
                } else if (p.compareDocumentPosition) {
                    return !!(p.compareDocumentPosition(c) & 16);
                } else {
                    while ((c = c.parentNode)) {
                        ret = c == p || ret;
                    }
                }
            }
            return ret;
        },

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

        
        unitizeBox: function(box, units) {
            var a = this.addUnits,
                b = this.parseBox(box);

            return a(b.top, units) + ' ' +
                   a(b.right, units) + ' ' +
                   a(b.bottom, units) + ' ' +
                   a(b.left, units);

        },

        
        camelReplaceFn: function(m, a) {
            return a.charAt(1).toUpperCase();
        },

        
        normalize: function(prop) {
            
            if (prop == 'float') {
                prop = Ext.supports.Float ? 'cssFloat' : 'styleFloat';
            }
            
            return this.propertyCache[prop] || (this.propertyCache[prop] = prop.replace(this.msRe, 'ms-').replace(this.camelRe, this.camelReplaceFn));
        },

        
        getDocumentHeight: function() {
            return Math.max(!Ext.isStrict ? document.body.scrollHeight : document.documentElement.scrollHeight, this.getViewportHeight());
        },

        
        getDocumentWidth: function() {
            return Math.max(!Ext.isStrict ? document.body.scrollWidth : document.documentElement.scrollWidth, this.getViewportWidth());
        },

        
        getViewportHeight: function(){
            return window.innerHeight;
        },

        
        getViewportWidth: function() {
            return window.innerWidth;
        },

        
        getViewSize: function() {
            return {
                width: window.innerWidth,
                height: window.innerHeight
            };
        },

        
        getOrientation: function() {
            if (Ext.supports.OrientationChange) {
                return (window.orientation == 0) ? 'portrait' : 'landscape';
            }

            return (window.innerHeight > window.innerWidth) ? 'portrait' : 'landscape';
        },

        
        fromPoint: function(x, y) {
            return Ext.get(document.elementFromPoint(x, y));
        },

        
        parseStyles: function(styles){
            var out = {},
                cssRe = this.cssRe,
                matches;

            if (styles) {
                
                
                
                
                cssRe.lastIndex = 0;
                while ((matches = cssRe.exec(styles))) {
                    out[matches[1]] = matches[2]||'';
                }
            }
            return out;
        }
    }
},
function () {
    var doc = document,
        activeElement = null,
        isCSS1 = doc.compatMode == "CSS1Compat";

    
    
    
    if (!('activeElement' in doc) && doc.addEventListener) {
        doc.addEventListener('focus',
            function (ev) {
                if (ev && ev.target) {
                    activeElement = (ev.target == doc) ? null : ev.target;
                }
            }, true);
    }

    
    function makeSelectionRestoreFn (activeEl, start, end) {
        return function () {
            activeEl.selectionStart = start;
            activeEl.selectionEnd = end;
        };
    }

    this.addInheritableStatics({
        
        getActiveElement: function () {
            var active;
            
            
            
            try {
                active = doc.activeElement;
            } catch(e) {}
            
            
            
            active = active || activeElement;
            if (!active) {
                active = activeElement = document.body;
            }
            return active;
        },

        
        getRightMarginFixCleaner: function (target) {
            var supports = Ext.supports,
                hasInputBug = supports.DisplayChangeInputSelectionBug,
                hasTextAreaBug = supports.DisplayChangeTextAreaSelectionBug,
                activeEl,
                tag,
                start,
                end;

            if (hasInputBug || hasTextAreaBug) {
                activeEl = doc.activeElement || activeElement; 
                tag = activeEl && activeEl.tagName;

                if ((hasTextAreaBug && tag == 'TEXTAREA') ||
                    (hasInputBug && tag == 'INPUT' && activeEl.type == 'text')) {
                    if (Ext.dom.Element.isAncestor(target, activeEl)) {
                        start = activeEl.selectionStart;
                        end = activeEl.selectionEnd;

                        if (Ext.isNumber(start) && Ext.isNumber(end)) { 
                            
                            
                            
                            
                            return makeSelectionRestoreFn(activeEl, start, end);
                        }
                    }
                }
            }

            return Ext.emptyFn; 
        },

        getViewWidth: function(full) {
            return full ? Ext.dom.Element.getDocumentWidth() : Ext.dom.Element.getViewportWidth();
        },

        getViewHeight: function(full) {
            return full ? Ext.dom.Element.getDocumentHeight() : Ext.dom.Element.getViewportHeight();
        },

        getDocumentHeight: function() {
            return Math.max(!isCSS1 ? doc.body.scrollHeight : doc.documentElement.scrollHeight, Ext.dom.Element.getViewportHeight());
        },

        getDocumentWidth: function() {
            return Math.max(!isCSS1 ? doc.body.scrollWidth : doc.documentElement.scrollWidth, Ext.dom.Element.getViewportWidth());
        },

        getViewportHeight: function(){
            return Ext.isIE9m ?
                   (Ext.isStrict ? doc.documentElement.clientHeight : doc.body.clientHeight) :
                   self.innerHeight;
        },

        getViewportWidth: function() {
            return (!Ext.isStrict && !Ext.isOpera) ? doc.body.clientWidth :
                   Ext.isIE9m ? doc.documentElement.clientWidth : self.innerWidth;
        },

        
        serializeForm: function(form) {
            var fElements = form.elements || (document.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 ? opt.hasAttribute('value') : opt.getAttributeNode('value').specified;
                                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);
        }
    });
});



Ext.define('Ext.dom.AbstractElement_insertion', {
    override: 'Ext.dom.AbstractElement',

    
    appendChild: function(el, returnDom) {
        var me = this,
            insertEl,
            eLen, e, oldUseDom;

        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(), '_internal');
            eLen = el.length;

            
            Ext.DomHelper.useDom = true;
            for (e = 0; e < eLen; e++) {
                insertEl.appendChild(el[e], returnDom);
            }
            Ext.DomHelper.useDom = oldUseDom;
            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;
    },

    
    insertBefore: function(el) {
        el = Ext.getDom(el);
        el.parentNode.insertBefore(this.dom, el);
        return this;
    },

    
    insertAfter: function(el) {
        el = Ext.getDom(el);
        el.parentNode.insertBefore(this.dom, el.nextSibling);
        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);
        }
    },

    
    insertSibling: function(el, where, returnDom) {
        var me        = this,
            DomHelper = Ext.core.DomHelper,
            oldUseDom = DomHelper.useDom,
            isAfter   = (where || 'before').toLowerCase() == 'after',
            rt, insertEl, eLen, e;

        if (Ext.isArray(el)) {
            
            insertEl = Ext.fly(document.createDocumentFragment(), '_internal');
            eLen = el.length;

            
            DomHelper.useDom = true;
            for (e = 0; e < eLen; e++) {
                rt = insertEl.appendChild(el[e], returnDom);
            }
            DomHelper.useDom = oldUseDom;

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

    
    replace: function(el) {
        el = Ext.get(el);
        this.insertBefore(el);
        el.remove();
        return this;
    },

    
    replaceWith: function(el){
        var me = this;

        if (el.nodeType || el.dom || typeof el == 'string') {
            el = Ext.get(el);
            me.dom.parentNode.insertBefore(el.dom, me.dom);
        } else {
            el = Ext.core.DomHelper.insertBefore(me.dom, el);
        }

        delete Ext.cache[me.id];
        Ext.removeNode(me.dom);
        me.id = Ext.id(me.dom = el);
        Ext.dom.AbstractElement.addToCache(me.isFlyweight ? new Ext.dom.AbstractElement(me.dom) : me);
        return me;
    },

    
    createChild: function(config, insertBefore, returnDom) {
        config = config || {tag:'div'};
        if (insertBefore) {
            return Ext.core.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
        }
        else {
            return Ext.core.DomHelper.append(this.dom, config,  returnDom !== true);
        }
    },

    
    wrap: function(config, returnDom, selector) {
        var newEl = Ext.core.DomHelper.insertBefore(this.dom, config || {tag: "div"}, true),
            target = newEl;
        
        if (selector) {
            target = Ext.DomQuery.selectNode(selector, newEl.dom);
        }

        target.appendChild(this.dom);
        return returnDom ? newEl.dom : newEl;
    },

    
    insertHtml: function(where, html, returnEl) {
        var el = Ext.core.DomHelper.insertHtml(where, this.dom, html);
        return returnEl ? Ext.get(el) : el;
    }
});



Ext.define('Ext.dom.AbstractElement_style', {

    override: 'Ext.dom.AbstractElement'

}, function() {
    
    var Element = this,
        wordsRe = /\w/g,
        spacesRe = /\s+/,
        transparentRe = /^(?:transparent|(?:rgba[(](?:\s*\d+\s*[,]){3}\s*0\s*[)]))$/i,
        
        
        
        
        hasClassList = Ext.supports.ClassList,
        PADDING = 'padding',
        MARGIN = 'margin',
        BORDER = 'border',
        LEFT_SUFFIX = '-left',
        RIGHT_SUFFIX = '-right',
        TOP_SUFFIX = '-top',
        BOTTOM_SUFFIX = '-bottom',
        WIDTH = '-width',
        
        borders = {l: BORDER + LEFT_SUFFIX + WIDTH, r: BORDER + RIGHT_SUFFIX + WIDTH, t: BORDER + TOP_SUFFIX + WIDTH, b: BORDER + BOTTOM_SUFFIX + WIDTH},
        paddings = {l: PADDING + LEFT_SUFFIX, r: PADDING + RIGHT_SUFFIX, t: PADDING + TOP_SUFFIX, b: PADDING + BOTTOM_SUFFIX},
        margins = {l: MARGIN + LEFT_SUFFIX, r: MARGIN + RIGHT_SUFFIX, t: MARGIN + TOP_SUFFIX, b: MARGIN + BOTTOM_SUFFIX},
        internalFly = new Element.Fly();


    Ext.override(Element, {

        
        styleHooks: {},

        
        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 += Math.abs(parseFloat(styleSides[styles[side]]) || 0);
                }
            }

            return totalSize;
        },

        
        addCls: (function(){
            var addWithClassList = function(className) {
                var me = this,
                    dom = me.dom,
                    trimRe = me.trimRe,
                    origClassName = className,
                    classList,
                    newCls,
                    i,
                    len,
                    cls;

                if (typeof(className) == 'string') {
                    
                    className = className.replace(trimRe, '').split(spacesRe);
                }

                
                
                if (dom && className && !!(len = className.length)) {
                    if (!dom.className) {
                        dom.className = className.join(' ');
                    } else {
                        classList = dom.classList;
                        if (classList) {
                            for (i = 0; i < len; ++i) {
                                cls = className[i];
                                if (cls) {
                                    if (!classList.contains(cls)) {
                                        if (newCls) {
                                            newCls.push(cls);
                                        } else {
                                            newCls = dom.className.replace(trimRe, '');
                                            newCls = newCls ? [newCls, cls] : [cls];
                                        }
                                    }
                                }
                            }

                            if (newCls) {
                                dom.className = newCls.join(' '); 
                            }
                        } else {
                            addWithoutClassList(origClassName);
                        }
                    }
                }
                return me;
            }, addWithoutClassList = function(className) {
                var me = this,
                    dom = me.dom,
                    elClasses;

                if (dom && className && className.length) {
                    elClasses = Ext.Element.mergeClsList(dom.className, className);
                    if (elClasses.changed) {
                        dom.className = elClasses.join(' '); 
                    }
                }
                return me;
            };
            
            return hasClassList ? addWithClassList : addWithoutClassList;
        })(),


        
        removeCls: function(className) {
            var me = this,
                dom = me.dom,
                classList,
                len,
                elClasses;

            if (typeof(className) == 'string') {
                
                className = className.replace(me.trimRe, '').split(spacesRe);
            }

            if (dom && dom.className && className && !!(len = className.length)) {
                classList = dom.classList;
                if (len === 1 && classList) {
                    if (className[0]) {
                        classList.remove(className[0]); 
                    }
                } else {
                    elClasses = Ext.Element.removeCls(dom.className, className);
                    if (elClasses.changed) {
                        dom.className = elClasses.join(' ');
                    }
                }
            }
            return me;
        },

        
        radioCls: function(className) {
            var cn = this.dom.parentNode.childNodes,
                v,
                i, len;
            className = Ext.isArray(className) ? className: [className];
            for (i = 0, len = cn.length; i < len; i++) {
                v = cn[i];
                if (v && v.nodeType == 1) {
                    internalFly.attach(v).removeCls(className);
                }
            }
            return this.addCls(className);
        },

        
        toggleCls: (function(){
            var toggleWithClassList = function(className){
                var me = this,
                    dom = me.dom,
                    classList;

                if (dom) {
                    className = className.replace(me.trimRe, '');
                    if (className) {
                        classList = dom.classList;
                        if (classList) {
                            classList.toggle(className);
                        } else {
                            toggleWithoutClassList(className);
                        }
                    }
                }

                return me;
            }, toggleWithoutClassList = function(className){
                return this.hasCls(className) ? this.removeCls(className) : this.addCls(className);
            };
            
            return hasClassList ? toggleWithClassList :  toggleWithoutClassList;
        })(),

        
        hasCls: (function(){
            var hasClsWithClassList = function(className) {
                var dom = this.dom,
                    out = false,
                    classList;
                    
                if (dom && className) {
                    classList = dom.classList;
                    if (classList) {
                        out = classList.contains(className);
                    } else {
                        out = hasClsWithoutClassList(className);
                    }
                }
                return out;
            }, hasClsWithoutClassList = function(className){
                var dom = this.dom;
                return dom ? className && (' '+dom.className+' ').indexOf(' '+className+' ') !== -1 : false;
            };
            
            return hasClassList ? hasClsWithClassList : hasClsWithoutClassList;
        })(),

        
        replaceCls: function(oldClassName, newClassName){
            return this.removeCls(oldClassName).addCls(newClassName);
        },

        
        isStyle: function(style, val) {
            return this.getStyle(style) == val;
        },

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

        getStyles: function () {
            var props = Ext.Array.slice(arguments),
                len = props.length,
                inline;

            if (len && typeof props[len-1] == 'boolean') {
                inline = props.pop();
            }

            return this.getStyle(props, inline);
        },

        
        isTransparent: function (prop) {
            var value = this.getStyle(prop);
            return value ? transparentRe.test(value) : false;
        },

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

        
        getHeight: function(contentHeight) {
            var dom = this.dom,
                height = contentHeight ? (dom.clientHeight - this.getPadding("tb")) : dom.offsetHeight;
            return height > 0 ? height: 0;
        },

        
        getWidth: function(contentWidth) {
            var dom = this.dom,
                width = contentWidth ? (dom.clientWidth - this.getPadding("lr")) : dom.offsetWidth;
            return width > 0 ? width: 0;
        },

        
        setWidth: function(width) {
            var me = this;
                me.dom.style.width = Element.addUnits(width);
            return me;
        },

        
        setHeight: function(height) {
            var me = this;
                me.dom.style.height = Element.addUnits(height);
            return me;
        },

        
        getBorderWidth: function(side){
            return this.addStyles(side, borders);
        },

        
        getPadding: function(side){
            return this.addStyles(side, paddings);
        },

        margins : margins,

        
        applyStyles: function(styles) {
            if (styles) {
                var i,
                    len,
                    dom = this.dom;

                if (typeof styles == 'function') {
                    styles = styles.call();
                }
                if (typeof styles == 'string') {
                    styles = Ext.util.Format.trim(styles).split(/\s*(?::|;)\s*/);
                    for (i = 0, len = styles.length; i < len;) {
                        dom.style[Element.normalize(styles[i++])] = styles[i++];
                    }
                }
                else if (typeof styles == 'object') {
                    this.setStyle(styles);
                }
            }
        },

        
        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);
            return me;
        },

        
        getViewSize: function() {
            var doc = document,
                dom = this.dom;

            if (dom == doc || dom == doc.body) {
                return {
                    width: Element.getViewportWidth(),
                    height: Element.getViewportHeight()
                };
            }
            else {
                return {
                    width: dom.clientWidth,
                    height: dom.clientHeight
                };
            }
        },

        
        getSize: function(contentSize) {
            var dom = this.dom;
            return {
                width: Math.max(0, contentSize ? (dom.clientWidth - this.getPadding("lr")) : dom.offsetWidth),
                height: Math.max(0, contentSize ? (dom.clientHeight - this.getPadding("tb")) : dom.offsetHeight)
            };
        },

        
        repaint: function() {
            var dom = this.dom;
            this.addCls(Ext.baseCSSPrefix + 'repaint');
            setTimeout(function(){
                internalFly.attach(dom).removeCls(Ext.baseCSSPrefix + 'repaint');
            }, 1);
            return this;
        },

        
        getMargin: function(side){
            var me = this,
                hash = {t:"top", l:"left", r:"right", b: "bottom"},
                key,
                o,
                margins;

            if (!side) {
                margins = [];
                for (key in me.margins) {
                    if(me.margins.hasOwnProperty(key)) {
                        margins.push(me.margins[key]);
                    }
                }
                o = me.getStyle(margins);
                if(o && typeof o == 'object') {
                    
                    for (key in me.margins) {
                        if(me.margins.hasOwnProperty(key)) {
                            o[hash[key]] = parseFloat(o[me.margins[key]]) || 0;
                        }
                    }
                }

                return o;
            } else {
                return me.addStyles(side, me.margins);
            }
        },

        
        mask: function(msg, msgCls, transparent) {
            var me = this,
                dom = me.dom,
                data = (me.$cache || me.getCache()).data,
                el = data.mask,
                mask,
                size,
                cls = '',
                prefix = Ext.baseCSSPrefix;

            me.addCls(prefix + 'masked');
            if (me.getStyle("position") == "static") {
                me.addCls(prefix + 'masked-relative');
            }
            if (el) {
                el.remove();
            }
            if (msgCls && typeof msgCls == 'string' ) {
                cls = ' ' + msgCls;
            }
            else {
                cls = ' ' + prefix + 'mask-gray';
            }

            mask = me.createChild({
                role: 'presentation',
                cls: prefix + 'mask' + ((transparent !== false) ? '' : (' ' + prefix + 'mask-gray')),
                html: msg ? ('<div class="' + (msgCls || (prefix + 'mask-message')) + '" role="presentation">' + msg + '</div>') : ''
            });

            size = me.getSize();

            data.mask = mask;

            if (dom === document.body) {
                size.height = window.innerHeight;
                if (me.orientationHandler) {
                    Ext.EventManager.unOrientationChange(me.orientationHandler, me);
                }

                me.orientationHandler = function() {
                    size = me.getSize();
                    size.height = window.innerHeight;
                    mask.setSize(size);
                };

                Ext.EventManager.onOrientationChange(me.orientationHandler, me);
            }
            mask.setSize(size);
            if (Ext.is.iPad) {
                Ext.repaint();
            }
        },

        
        unmask: function() {
            var me = this,
                data = (me.$cache || me.getCache()).data,
                mask = data.mask,
                prefix = Ext.baseCSSPrefix;

            if (mask) {
                mask.remove();
                delete data.mask;
            }
            me.removeCls([prefix + 'masked', prefix + 'masked-relative']);

            if (me.dom === document.body) {
                Ext.EventManager.unOrientationChange(me.orientationHandler, me);
                delete me.orientationHandler;
            }
        }
    });


    Ext.onReady(function () {
        var supports = Ext.supports,
            styleHooks,
            colorStyles, i, name, camel;

        function fixTransparent (dom, el, inline, style) {
            var value = style[this.name] || '';
            return transparentRe.test(value) ? 'transparent' : value;
        }

        function fixRightMargin (dom, el, inline, style) {
            var result = style.marginRight,
                domStyle, display;

            
            
            if (result != '0px') {
                domStyle = dom.style;
                display = domStyle.display;
                domStyle.display = 'inline-block';
                result = (inline ? style : dom.ownerDocument.defaultView.getComputedStyle(dom, null)).marginRight;
                domStyle.display = display;
            }

            return result;
        }

        function fixRightMarginAndInputFocus (dom, el, inline, style) {
            var result = style.marginRight,
                domStyle, cleaner, display;

            if (result != '0px') {
                domStyle = dom.style;
                cleaner = Element.getRightMarginFixCleaner(dom);
                display = domStyle.display;
                domStyle.display = 'inline-block';
                result = (inline ? style : dom.ownerDocument.defaultView.getComputedStyle(dom, '')).marginRight;
                domStyle.display = display;
                cleaner();
            }

            return result;
        }

        styleHooks = Element.prototype.styleHooks;

        
        
        if (supports.init) {
            supports.init();
        }

        
        if (!supports.RightMargin) {
            styleHooks.marginRight = styleHooks['margin-right'] = {
                name: 'marginRight',
                
                
                get: (supports.DisplayChangeInputSelectionBug || supports.DisplayChangeTextAreaSelectionBug) ?
                    fixRightMarginAndInputFocus : fixRightMargin
            };
        }

        if (!supports.TransparentColor) {
            colorStyles = ['background-color', 'border-color', 'color', 'outline-color'];
            for (i = colorStyles.length; i--; ) {
                name = colorStyles[i];
                camel = Element.normalize(name);

                styleHooks[name] = styleHooks[camel] = {
                    name: camel,
                    get: fixTransparent
                };
            }
        }
    });

});



Ext.define('Ext.dom.AbstractElement_traversal', {
    override: 'Ext.dom.AbstractElement',

    
    findParent: function(simpleSelector, limit, returnEl) {
        var target = this.dom,
            topmost = document.documentElement,
            depth = 0,
            stopEl;

        limit = limit || 50;
        if (isNaN(limit)) {
            stopEl = Ext.getDom(limit);
            limit = Number.MAX_VALUE;
        }
        while (target && target.nodeType == 1 && depth < limit && target != topmost && target != stopEl) {
            if (Ext.DomQuery.is(target, 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, '_internal');
        return p ? p.findParent(simpleSelector, limit, returnEl) : null;
    },

    
    up: function(simpleSelector, limit, returnDom) {
        return this.findParentNode(simpleSelector, limit, !returnDom);
    },

    
    select: function(selector, composite) {
        return Ext.dom.Element.select(selector, this.dom, composite);
    },

    
    query: function(selector) {
        return Ext.DomQuery.select(selector, this.dom);
    },

    
    down: function(selector, returnDom) {
        var n = Ext.DomQuery.selectNode(selector, this.dom);
        return returnDom ? n : Ext.get(n);
    },

    
    child: function(selector, returnDom) {
        var node,
            me = this,
            id;

        
        
        id = Ext.id(me.dom);
        
        id = Ext.escapeId(id);
        node = Ext.DomQuery.selectNode('#' + id + " > " + selector, me.dom);
        return returnDom ? node : Ext.get(node);
    },

     
    parent: function(selector, returnDom) {
        return this.matchNode('parentNode', 'parentNode', selector, returnDom);
    },

     
    next: function(selector, returnDom) {
        return this.matchNode('nextSibling', 'nextSibling', selector, returnDom);
    },

    
    prev: function(selector, returnDom) {
        return this.matchNode('previousSibling', 'previousSibling', selector, returnDom);
    },


    
    first: function(selector, returnDom) {
        return this.matchNode('nextSibling', 'firstChild', selector, returnDom);
    },

    
    last: function(selector, returnDom) {
        return this.matchNode('previousSibling', 'lastChild', selector, returnDom);
    },

    matchNode: function(dir, start, selector, returnDom) {
        if (!this.dom) {
            return null;
        }

        var n = this.dom[start];
        while (n) {
            if (n.nodeType == 1 && (!selector || Ext.DomQuery.is(n, selector))) {
                return !returnDom ? Ext.get(n) : n;
            }
            n = n[dir];
        }
        return null;
    },

    isAncestor: function(element) {
        return this.self.isAncestor.call(this.self, this.dom, element);
    }
});





Ext.define('Ext.dom.AbstractElement', {
               
                           
                                         
                                            
                                        
                                           
      

    trimRe: /^\s+|\s+$/g,
    whitespaceRe: /\s/,
    
    inheritableStatics: {
        trimRe: /^\s+|\s+$/g,
        whitespaceRe: /\s/,

        
        get: function(el) {
            var me = this,
                document = window.document,
                El = Ext.dom.Element,
                cacheItem,
                docEl,
                extEl,
                dom,
                id;

            if (!el) {
                return null;
            }

            
            if (el.isFly) {
                el = el.dom;
            }

            if (typeof el == "string") { 
                if (el == Ext.windowId) {
                    return El.get(window);
                } else if (el == Ext.documentId) {
                    return El.get(document);
                }
                
                cacheItem = Ext.cache[el];
                
                
                
                
                if (cacheItem && cacheItem.skipGarbageCollection) {
                    extEl = cacheItem.el;
                    return extEl;
                }
                
                if (!(dom = document.getElementById(el))) {
                    return null;
                }

                if (cacheItem && cacheItem.el) {
                    extEl = Ext.updateCacheEntry(cacheItem, dom).el;
                } else {
                    
                    extEl = new El(dom, !!cacheItem);
                }
                return extEl;
            } else if (el.tagName) { 
                if (!(id = el.id)) {
                    id = Ext.id(el);
                }
                cacheItem = Ext.cache[id];
                if (cacheItem && cacheItem.el) {
                    extEl = Ext.updateCacheEntry(cacheItem, el).el;
                } else {
                    
                    extEl = new El(el, !!cacheItem);
                }
                return extEl;
            } else if (el instanceof me) {
                if (el != me.docEl && el != me.winEl) {
                    id = el.id;
                    
                    
                    cacheItem = Ext.cache[id];
                    if (cacheItem) {
                        Ext.updateCacheEntry(cacheItem, document.getElementById(id) || el.dom);
                    }
                }
                return el;
            } else if (el.isComposite) {
                return el;
            } else if (Ext.isArray(el)) {
                return me.select(el);
            } else if (el === document) {
                
                if (!me.docEl) {
                    docEl = me.docEl = Ext.Object.chain(El.prototype);
                    docEl.dom = document;
                    
                    
                    
                    docEl.el = docEl;
                    docEl.id = Ext.id(document);
                    me.addToCache(docEl);
                }
                return me.docEl;
            } else if (el === window) {
                if (!me.winEl) {
                    me.winEl = Ext.Object.chain(El.prototype);
                    me.winEl.dom = window;
                    me.winEl.id = Ext.id(window);
                    me.addToCache(me.winEl);
                }
                return me.winEl;
            }
            return null;
        },

        addToCache: function(el, id) {
            if (el) {
                Ext.addCacheEntry(id, el);
            }
            return el;
        },

        addMethods: function() {
            this.override.apply(this, arguments);
        },

        
        mergeClsList: function() {
            var clsList, clsHash = {},
                i, length, j, listLength, clsName, result = [],
                changed = false,
                trimRe = this.trimRe,
                whitespaceRe = this.whitespaceRe;

            for (i = 0, length = arguments.length; i < length; i++) {
                clsList = arguments[i];
                if (Ext.isString(clsList)) {
                    clsList = clsList.replace(trimRe, '').split(whitespaceRe);
                }
                if (clsList) {
                    for (j = 0, listLength = clsList.length; j < listLength; j++) {
                        clsName = clsList[j];
                        if (!clsHash[clsName]) {
                            if (i) {
                                changed = true;
                            }
                            clsHash[clsName] = true;
                        }
                    }
                }
            }

            for (clsName in clsHash) {
                result.push(clsName);
            }
            result.changed = changed;
            return result;
        },

        
        removeCls: function(existingClsList, removeClsList) {
            var clsHash = {},
                i, length, clsName, result = [],
                changed = false,
                whitespaceRe = this.whitespaceRe;

            if (existingClsList) {
                if (Ext.isString(existingClsList)) {
                    existingClsList = existingClsList.replace(this.trimRe, '').split(whitespaceRe);
                }
                for (i = 0, length = existingClsList.length; i < length; i++) {
                    clsHash[existingClsList[i]] = true;
                }
            }
            if (removeClsList) {
                if (Ext.isString(removeClsList)) {
                    removeClsList = removeClsList.split(whitespaceRe);
                }
                for (i = 0, length = removeClsList.length; i < length; i++) {
                    clsName = removeClsList[i];
                    if (clsHash[clsName]) {
                        changed = true;
                        delete clsHash[clsName];
                    }
                }
            }
            for (clsName in clsHash) {
                result.push(clsName);
            }
            result.changed = changed;
            return result;
        },

        
        VISIBILITY: 1,

        
        DISPLAY: 2,

        
        OFFSETS: 3,

        
        ASCLASS: 4
    },

    constructor: function(element, forceNew) {
        var me = this,
            dom = typeof element == 'string'
                ? document.getElementById(element)
                : element,
            id;

        
        
        
        me.el = me;

        if (!dom) {
            return null;
        }

        id = dom.id;
        if (!forceNew && id && Ext.cache[id]) {
            
            return Ext.cache[id].el;
        }

        
        me.dom = dom;

        
        me.id = id || Ext.id(dom);

        me.self.addToCache(me);
    },

    
    set: function(o, useSet) {
         var el = this.dom,
             attr,
             value;

         for (attr in o) {
             if (o.hasOwnProperty(attr)) {
                 value = o[attr];
                 if (attr == 'style') {
                     this.applyStyles(value);
                 }
                 else if (attr == 'cls') {
                     el.className = value;
                 }
                 else if (useSet !== false) {
                     if (value === undefined) {
                         el.removeAttribute(attr);
                     } else {
                        el.setAttribute(attr, value);
                     }
                 }
                 else {
                     el[attr] = value;
                 }
             }
         }
         return this;
     },

    
    defaultUnit: "px",

    
    is: function(simpleSelector) {
        return Ext.DomQuery.is(this.dom, simpleSelector);
    },

    
    getValue: function(asNumber) {
        var val = this.dom.value;
        return asNumber ? parseInt(val, 10) : val;
    },

    
    remove: function() {
        var me = this,
            dom = me.dom;
            
        if (me.isAnimate) {
            me.stopAnimation();
        }

        if (dom) {
            Ext.removeNode(dom);
            delete me.dom;
        }
    },

    
    contains: (function () {
        
        
        
        
        var isXpc = function (el) {
            var s;

            try {
                el = el.dom || el;
            } catch (e) {
                return true;
            }

            s = HTMLElement.prototype.toString.call(el);
            return s === '[xpconnect wrapped native prototype]' || s === '[object XULElement]';
        };

        return function (el) {
            if (!el || (Ext.isGecko3 && isXpc(el))) {
                return false;
            }

            var me = this,
                dom = el.dom || el;

            
            return (dom === me.dom) || Ext.dom.AbstractElement.isAncestor(me.dom, dom);
        };
    }()),

    
    getAttribute: function(name, ns) {
        var dom = this.dom;
        return dom.getAttributeNS(ns, name) || dom.getAttribute(ns + ":" + name) || dom.getAttribute(name) || dom[name];
    },

    
    update: function(html) {
        if (this.dom) {
            this.dom.innerHTML = html;
        }
        return this;
    },


    
    setHTML: function(html) {
        if(this.dom) {
            this.dom.innerHTML = html;
        }
        return this;
    },

    
    getHTML: function() {
        return this.dom ? this.dom.innerHTML : '';
    },

    
    hide: function() {
        this.setVisible(false);
        return this;
    },

    
    show: function() {
        this.setVisible(true);
        return this;
    },

    
    setVisible: function(visible, animate) {
        var me = this,
            statics = me.self,
            mode = me.getVisibilityMode(),
            prefix = Ext.baseCSSPrefix;

        switch (mode) {
            case statics.VISIBILITY:
                me.removeCls([prefix + 'hidden-display', prefix + 'hidden-offsets']);
                me[visible ? 'removeCls' : 'addCls'](prefix + 'hidden-visibility');
            break;

            case statics.DISPLAY:
                me.removeCls([prefix + 'hidden-visibility', prefix + 'hidden-offsets']);
                me[visible ? 'removeCls' : 'addCls'](prefix + 'hidden-display');
            break;

            case statics.OFFSETS:
                me.removeCls([prefix + 'hidden-visibility', prefix + 'hidden-display']);
                me[visible ? 'removeCls' : 'addCls'](prefix + 'hidden-offsets');
            break;
        }

        return me;
    },

    getVisibilityMode: function() {
        
        
        
        var data = (this.$cache || this.getCache()).data,
            visMode = data.visibilityMode;

        if (visMode === undefined) {
            data.visibilityMode = visMode = this.self.DISPLAY;
        }
        
        return visMode;
    },

    
    setVisibilityMode: function(mode) {
        (this.$cache || this.getCache()).data.visibilityMode = mode;
        return this;
    },
    
    getCache: function() {
        var me = this,
            id = me.dom.id || Ext.id(me.dom);

        
        
        
        me.$cache = Ext.cache[id] || Ext.addCacheEntry(id, null, me.dom);
            
        return me.$cache;
    }
},
function() {
    var AbstractElement = this;

    
    Ext.getDetachedBody = function () {
        var detachedEl = AbstractElement.detachedBodyEl;

        if (!detachedEl) {
            detachedEl = document.createElement('div');
            AbstractElement.detachedBodyEl = detachedEl = new AbstractElement.Fly(detachedEl);
            detachedEl.isDetachedBody = true;
        }

        return detachedEl;
    };

    
    Ext.getElementById = function (id) {
        var el = document.getElementById(id),
            detachedBodyEl;

        if (!el && (detachedBodyEl = AbstractElement.detachedBodyEl)) {
            el = detachedBodyEl.dom.querySelector('#' + Ext.escapeId(id));
        }

        return el;
    };

    
    Ext.get = function(el) {
        return Ext.dom.Element.get(el);
    };

    this.addStatics({
        
        Fly: new Ext.Class({
            
            
            
            
            extend: AbstractElement,

            
            isFly: true,

            constructor: function(dom) {
                this.dom = dom;
                
                
                
                this.el = this;
            },

            
            attach: function (dom) {

                
                this.dom = dom;
                
                
                this.$cache = dom && dom.id ? Ext.cache[dom.id] : null;
                return this;
            }
        }),

        _flyweights: {},

        
        fly: function(dom, named) {
            var fly = null,
                _flyweights = AbstractElement._flyweights;

            named = named || '_global';

            dom = Ext.getDom(dom);

            if (dom) {
                fly = _flyweights[named] || (_flyweights[named] = new AbstractElement.Fly());

                
                
                fly.dom = dom;
                
                
                fly.$cache = dom.id ? Ext.cache[dom.id] : null;
            }
            return fly;
        }
    });

    
    Ext.fly = function() {
        return AbstractElement.fly.apply(AbstractElement, arguments);
    };

    (function (proto) {
        
        proto.destroy = proto.remove;

        
        if (document.querySelector) {
            proto.getById = function (id, asDom) {
                
                
                var dom = document.getElementById(id) ||
                    this.dom.querySelector('#'+Ext.escapeId(id));
                return asDom ? dom : (dom ? Ext.get(dom) : null);
            };
        } else {
            proto.getById = function (id, asDom) {
                var dom = document.getElementById(id);
                return asDom ? dom : (dom ? Ext.get(dom) : null);
            };
        }
    }(this.prototype));
});







Ext.define('Ext.dom.Helper', (function() {


var afterbegin = 'afterbegin',
    afterend = 'afterend',
    beforebegin = 'beforebegin',
    beforeend = 'beforeend',
    ts = '<table>',
    te = '</table>',
    tbs = ts+'<tbody>',
    tbe = '</tbody>'+te,
    trs = tbs + '<tr>',
    tre = '</tr>'+tbe,
    detachedDiv = document.createElement('div'),
    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 {
    extend:  Ext.dom.AbstractHelper ,
                                         

    tableRe: /^(?:table|thead|tbody|tr|td)$/i,

    tableElRe: /td|tr|tbody|thead/i,

    
    useDom : false,

    
    createDom: function(o, parentNode){
        var el,
            doc = document,
            useSet,
            attr,
            val,
            cn,
            i, l;

        if (Ext.isArray(o)) {                       
            el = doc.createDocumentFragment(); 
            for (i = 0, l = o.length; i < l; i++) {
                this.createDom(o[i], el);
            }
        } else if (typeof o == 'string') {         
            el = doc.createTextNode(o);
        } else {
            el = doc.createElement(o.tag || 'div');
            useSet = !!el.setAttribute; 
            for (attr in o) {
                if (!this.confRe.test(attr)) {
                    val = o[attr];
                    if (attr == 'cls') {
                        el.className = val;
                    } else {
                        if (useSet) {
                            el.setAttribute(attr, val);
                        } else {
                            el[attr] = val;
                        }
                    }
                }
            }
            Ext.DomHelper.applyStyles(el, o.style);

            if ((cn = o.children || o.cn)) {
                this.createDom(cn, el);
            } else if (o.html) {
                el.innerHTML = o.html;
            }
        }
        if (parentNode) {
            parentNode.appendChild(el);
        }
        return el;
    },

    ieTable: function(depth, openingTags, htmlContent, closingTags){
        detachedDiv.innerHTML = [openingTags, htmlContent, closingTags].join('');

        var i = -1,
            el = detachedDiv,
            ns;

        while (++i < depth) {
            el = el.firstChild;
        }
        
        ns = el.nextSibling;

        if (ns) {
            ns = el;
            el = document.createDocumentFragment();
            
            while (ns) {
                 nx = ns.nextSibling;
                 el.appendChild(ns);
                 ns = nx;
            }
        }
        return el;
    },

    
    insertIntoTable: function(tag, where, destinationEl, html) {
        var node,
            before,
            bb = where == beforebegin,
            ab = where == afterbegin,
            be = where == beforeend,
            ae = where == afterend;

        if (tag == 'td' && (ab || be) || !this.tableElRe.test(tag) && (bb || ae)) {
            return null;
        }
        before = bb ? destinationEl :
                 ae ? destinationEl.nextSibling :
                 ab ? destinationEl.firstChild : null;

        if (bb || ae) {
            destinationEl = destinationEl.parentNode;
        }

        if (tag == 'td' || (tag == 'tr' && (be || ab))) {
            node = this.ieTable(4, trs, html, tre);
        } else if (((tag == 'tbody' || tag == 'thead') && (be || ab)) ||
                (tag == 'tr' && (bb || ae))) {
            node = this.ieTable(3, tbs, html, tbe);
        } else {
            node = this.ieTable(2, ts, html, te);
        }
        destinationEl.insertBefore(node, before);
        return node;
    },

    
    createContextualFragment: function(html) {
        var fragment = document.createDocumentFragment(),
            length, childNodes;

        detachedDiv.innerHTML = html;
        childNodes = detachedDiv.childNodes;
        length = childNodes.length;

        
        while (length--) {
            fragment.appendChild(childNodes[0]);
        }
        return fragment;
    },

    applyStyles: function(el, styles) {
        if (styles) {
            if (typeof styles == "function") {
                styles = styles.call();
            }
            if (typeof styles == "string") {
                styles = Ext.dom.Element.parseStyles(styles);
            }
            if (typeof styles == "object") {
                Ext.fly(el, '_applyStyles').setStyle(styles);
            }
        }
    },

    
    createHtml: function(spec) {
        return this.markup(spec);
    },

    doInsert: function(el, o, returnElement, pos, sibling, append) {
        
        el = el.dom || Ext.getDom(el);

        var newNode;

        if (this.useDom) {
            newNode = this.createDom(o, null);

            if (append) {
                el.appendChild(newNode);
            }
            else {
                (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
            }

        } else {
            newNode = this.insertHtml(pos, el, this.markup(o));
        }
        return returnElement ? Ext.get(newNode, true) : newNode;
    },

    
    overwrite: function(el, html, returnElement) {
        var newNode;

        el = Ext.getDom(el);
        html = this.markup(html);

        
        if (Ext.isIE && this.tableRe.test(el.tagName)) {
            
            while (el.firstChild) {
                el.removeChild(el.firstChild);
            }
            if (html) {
                newNode = this.insertHtml('afterbegin', el, html);
                return returnElement ? Ext.get(newNode) : newNode;
            }
            return null;
        }
        el.innerHTML = html;
        return returnElement ? Ext.get(el.firstChild) : el.firstChild;
    },

    insertHtml: function(where, el, html) {
        var hashVal,
            range,
            rangeEl,
            setStart,
            frag;

        where = where.toLowerCase();

        
        if (el.insertAdjacentHTML) {

            
            if (Ext.isIE && this.tableRe.test(el.tagName) && (frag = this.insertIntoTable(el.tagName.toLowerCase(), where, el, html))) {
                return frag;
            }

            if ((hashVal = fullPositionHash[where])) {

                if (Ext.global.MSApp && Ext.global.MSApp.execUnsafeLocalFunction) {
                    
                    MSApp.execUnsafeLocalFunction(function () {
                        el.insertAdjacentHTML(hashVal[0], html);
                    });
                } else {
                    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) {
                        range[setStart](el[rangeEl]);
                        frag = range.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];
            }
        }
    },

    
    createTemplate: function(o) {
        var html = this.markup(o);
        return new Ext.Template(html);
    }

};
})(), function() {
    Ext.ns('Ext.core');
    Ext.DomHelper = Ext.core.DomHelper = new this;
});



Ext.define('Ext.Template', {

    

                                                    

    inheritableStatics: {
        
        from: function(el, config) {
            el = Ext.getDom(el);
            return new this(el.value || el.innerHTML, config || '');
        }
    },

    

    
    constructor: function(html) {
        var me = this,
            args = arguments,
            buffer = [],
            i = 0,
            length = args.length,
            value;

        me.initialConfig = {};
        
        
        
        
        if (length === 1 && Ext.isArray(html)) {
            args = html;
            length = args.length;
        }

        if (length > 1) {
            for (; 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('');

        if (me.compiled) {
            me.compile();
        }
    },

    
    isTemplate: true,

    

    
    disableFormats: false,

    re: /\{([\w\-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,

    
    apply: function(values) {
        var me = this,
            useFormat = me.disableFormats !== true,
            fm = Ext.util.Format,
            tpl = me,
            ret;

        if (me.compiled) {
            return me.compiled(values).join('');
        }

        function fn(m, name, format, args) {
            if (format && useFormat) {
                if (args) {
                    args = [values[name]].concat(Ext.functionFactory('return ['+ args +'];')());
                } else {
                    args = [values[name]];
                }
                if (format.substr(0, 5) == "this.") {
                    return tpl[format.substr(5)].apply(tpl, args);
                }
                else {
                    return fm[format].apply(fm, args);
                }
            }
            else {
                return values[name] !== undefined ? values[name] : "";
            }
        }

        ret = me.html.replace(me.re, fn);
        return ret;
    },

    
    applyOut: function(values, out) {
        var me = this;

        if (me.compiled) {
            out.push.apply(out, me.compiled(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 = null;
        return compile ? me.compile() : me;
    },

    compileARe: /\\/g,
    compileBRe: /(\r\n|\n)/g,
    compileCRe: /'/g,

    /**
     * Compiles the template into an internal function, eliminating the RegEx overhead.
     * @return {Ext.Template} this
     */
    compile: function() {
        var me = this,
            fm = Ext.util.Format,
            useFormat = me.disableFormats !== true,
            body, bodyReturn;

        function fn(m, name, format, args) {
            if (format && useFormat) {
                args = args ? ',' + args: "";
                if (format.substr(0, 5) != "this.") {
                    format = "fm." + format + '(';
                }
                else {
                    format = 'this.' + format.substr(5) + '(';
                }
            }
            else {
                args = '';
                format = "(values['" + name + "'] == undefined ? '' : ";
            }
            return "'," + format + "values['" + name + "']" + args + ") ,'";
        }

        bodyReturn = me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn);
        body = "this.compiled = function(values){ return ['" + bodyReturn + "'];};";
        eval(body);
        return me;
    },

    /**
     * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
     *
     * @param {String/HTMLElement/Ext.Element} el The context element
     * @param {Object/Array} values The template values. See {@link #applyTemplate} for details.
     * @param {Boolean} returnElement (optional) true to return a Ext.Element.
     * @return {HTMLElement/Ext.Element} The new node or Element
     */
    insertFirst: function(el, values, returnElement) {
        return this.doInsert('afterBegin', el, values, returnElement);
    },

    /**
     * Applies the supplied values to the template and inserts the new node(s) before el.
     *
     * @param {String/HTMLElement/Ext.Element} el The context element
     * @param {Object/Array} values The template values. See {@link #applyTemplate} for details.
     * @param {Boolean} returnElement (optional) true to return a Ext.Element.
     * @return {HTMLElement/Ext.Element} The new node or Element
     */
    insertBefore: function(el, values, returnElement) {
        return this.doInsert('beforeBegin', el, values, returnElement);
    },

    /**
     * Applies the supplied values to the template and inserts the new node(s) after el.
     *
     * @param {String/HTMLElement/Ext.Element} el The context element
     * @param {Object/Array} values The template values. See {@link #applyTemplate} for details.
     * @param {Boolean} returnElement (optional) true to return a Ext.Element.
     * @return {HTMLElement/Ext.Element} The new node or Element
     */
    insertAfter: function(el, values, returnElement) {
        return this.doInsert('afterEnd', el, values, returnElement);
    },

    /**
     * Applies the supplied `values` to the template and appends the new node(s) to the specified `el`.
     *
     * For example usage see {@link Ext.Template Ext.Template class docs}.
     *
     * @param {String/HTMLElement/Ext.Element} el The context element
     * @param {Object/Array} values The template values. See {@link #applyTemplate} for details.
     * @param {Boolean} returnElement (optional) true to return an Ext.Element.
     * @return {HTMLElement/Ext.Element} The new node or Element
     */
    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;
    },

    /**
     * Applies the supplied values to the template and overwrites the content of el with the new node(s).
     *
     * @param {String/HTMLElement/Ext.Element} el The context element
     * @param {Object/Array} values The template values. See {@link #applyTemplate} for details.
     * @param {Boolean} returnElement (optional) true to return a Ext.Element.
     * @return {HTMLElement/Ext.Element} The new node or Element
     */
    overwrite: function(el, values, returnElement) {
        var newNode = Ext.DomHelper.overwrite(Ext.getDom(el), this.apply(values));
        return returnElement ? Ext.get(newNode) : newNode;
    }
});

// @tag core
/**
 * This class parses the XTemplate syntax and calls abstract methods to process the parts.
 * @private
 */
Ext.define('Ext.XTemplateParser', {
    constructor: function (config) {
        Ext.apply(this, config);
    },

    /**
     * @property {Number} level The 'for' or 'foreach' loop context level. This is adjusted
     * up by one prior to calling {@link #doFor} or {@link #doForEach} and down by one after
     * calling the corresponding {@link #doEnd} that closes the loop. This will be 1 on the
     * first {@link #doFor} or {@link #doForEach} call.
     */

    /**
     * This method is called to process a piece of raw text from the tpl.
     * @param {String} text
     * @method doText
     */
    // doText: function (text)

    /**
     * This method is called to process expressions (like `{[expr]}`).
     * @param {String} expr The body of the expression (inside "{[" and "]}").
     * @method doExpr
     */
    // doExpr: function (expr)

    /**
     * This method is called to process simple tags (like `{tag}`).
     * @method doTag
     */
    // doTag: function (tag)

    /**
     * This method is called to process `<tpl else>`.
     * @method doElse
     */
    // doElse: function ()

    /**
     * This method is called to process `{% text %}`.
     * @param {String} text
     * @method doEval
     */
    // doEval: function (text)

    /**
     * This method is called to process `<tpl if="action">`. If there are other attributes,
     * these are passed in the actions object.
     * @param {String} action
     * @param {Object} actions Other actions keyed by the attribute name (such as 'exec').
     * @method doIf
     */
    // doIf: function (action, actions)

    /**
     * This method is called to process `<tpl elseif="action">`. If there are other attributes,
     * these are passed in the actions object.
     * @param {String} action
     * @param {Object} actions Other actions keyed by the attribute name (such as 'exec').
     * @method doElseIf
     */
    // doElseIf: function (action, actions)

    /**
     * This method is called to process `<tpl switch="action">`. If there are other attributes,
     * these are passed in the actions object.
     * @param {String} action
     * @param {Object} actions Other actions keyed by the attribute name (such as 'exec').
     * @method doSwitch
     */
    // doSwitch: function (action, actions)

    /**
     * This method is called to process `<tpl case="action">`. If there are other attributes,
     * these are passed in the actions object.
     * @param {String} action
     * @param {Object} actions Other actions keyed by the attribute name (such as 'exec').
     * @method doCase
     */
    // doCase: function (action, actions)

    /**
     * This method is called to process `<tpl default>`.
     * @method doDefault
     */
    // doDefault: function ()

    /**
     * This method is called to process `</tpl>`. It is given the action type that started
     * the tpl and the set of additional actions.
     * @param {String} type The type of action that is being ended.
     * @param {Object} actions The other actions keyed by the attribute name (such as 'exec').
     * @method doEnd
     */
    // doEnd: function (type, actions) 

    /**
     * This method is called to process `<tpl for="action">`. If there are other attributes,
     * these are passed in the actions object.
     * @param {String} action
     * @param {Object} actions Other actions keyed by the attribute name (such as 'exec').
     * @method doFor
     */
    // doFor: function (action, actions)

    /**
     * This method is called to process `<tpl foreach="action">`. If there are other
     * attributes, these are passed in the actions object.
     * @param {String} action
     * @param {Object} actions Other actions keyed by the attribute name (such as 'exec').
     * @method doForEach
     */
    // doForEach: function (action, actions)

    /**
     * This method is called to process `<tpl exec="action">`. If there are other attributes,
     * these are passed in the actions object.
     * @param {String} action
     * @param {Object} actions Other actions keyed by the attribute name.
     * @method doExec
     */
    // doExec: function (action, actions)

    /**
     * This method is called to process an empty `<tpl>`. This is unlikely to need to be
     * implemented, so a default (do nothing) version is provided.
     * @method
     */
    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;

        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) {
                me.doText(str.substring(index, begin));
            }

            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]) { // if ('{' token)
                me.doTag(m[3]);
            } else if (m[4]) { // content of a <tpl xxxxxx xxx> tag
                actions = null;
                while ((subMatch = actionsRe.exec(m[4])) !== null) {
                    s = subMatch[2] || subMatch[3];
                    if (s) {
                        s = Ext.String.htmlDecode(s); // decode attr value
                        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' });
                }
                else if (actions['case']) {
                    me.doCase(actions['case'], actions);
                }
                else if (actions['elif']) {
                    me.doElseIf(actions['elif'], actions);
                }
                else if (actions['for']) {
                    ++me.level;

                    // Extract property name to use from indexed item
                    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;

                    // Extract property name to use from indexed item
                    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 {
                    // todo - error
                }
                */
            } else if (m[0].length === 5) {
                // if the length of m[0] is 5, assume that we're dealing with an opening tpl tag with no attributes (e.g. <tpl>...</tpl>)
                // in this case no action is needed other than pushing it on to the stack
                stack.push({ type: 'tpl' });
            } else {
                frame = stack.pop();
                me.doEnd(frame.type, frame.actions);
                if (frame.type == 'for' || frame.type == 'foreach') {
                    --me.level;
                }
            }
        }
    },

    // Internal regexes
    
    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.XTemplateCompiler', {
    extend:  Ext.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;

        
        if (action === '.') {
            me.body.push('switch (values) {\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;

        me.definitions.push('function ' + name + '(' + me.fnArgs + ') {',
                            ' try { with(values) {',
                            '  ' + action,
                            ' }} catch(e) {',
                            '}',
                      '}');

        me.body.push(name + me.callFn + '\n');
    },

    
    

    addFn: function (body) {
        var me = this,
            name = 'f' + me.definitions.length;

        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 + ') {',
                            ' try { with(values) {',
                            '  return(' + body + ')',
                            ' }} catch(e) {',
                            '}',
                       '}');
        }

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

// @tag core
/**
 * A template class that supports advanced functionality like:
 *
 * - Autofilling arrays using templates and sub-templates
 * - Conditional processing with basic comparison operators
 * - Basic math function support
 * - Execute arbitrary inline code with special built-in template variables
 * - Custom member functions
 * - Many special tags and built-in operators that aren't defined as part of the API, but are supported in the templates that can be created
 *
 * XTemplate provides the templating mechanism built into {@link Ext.view.View}.
 *
 * The {@link Ext.Template} describes the acceptable parameters to pass to the constructor. The following examples
 * demonstrate all of the supported features.
 *
 * # Sample Data
 *
 * This is the data object used for reference in each code example:
 *
 *     var data = {
 *         name: 'Don Griffin',
 *         title: 'Senior Technomage',
 *         company: 'Sencha Inc.',
 *         drinks: ['Coffee', 'Water', 'More Coffee'],
 *         kids: [
 *             { name: 'Aubrey',  age: 17 },
 *             { name: 'Joshua',  age: 13 },
 *             { name: 'Cale',    age: 10 },
 *             { name: 'Nikol',   age: 5 },
 *             { name: 'Solomon', age: 0 }
 *         ]
 *     };
 *
 * # Auto filling of arrays
 *
 * The **tpl** tag and the **for** operator are used to process the provided data object:
 *
 * - If the value specified in for is an array, it will auto-fill, repeating the template block inside the tpl
 *   tag for each item in the array.
 * - If for="." is specified, the data object provided is examined.
 * - If between="..." is specified, the provided value will be inserted between the items.
 *   This is also supported in the "foreach" looping template.
 * - While processing an array, the special variable {#} will provide the current array index + 1 (starts at 1, not 0).
 *
 * Examples:
 *
 *     <tpl for=".">...</tpl>       
 *     <tpl for="foo">...</tpl>     
 *     <tpl for="foo.bar">...</tpl> 
 *     <tpl for="." between=",">...</tpl> 
 *
 * Using the sample data above:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Kids: ',
 *         '<tpl for=".">',       
 *             '<p>{#}. {name}</p>',  
 *         '</tpl></p>'
 *     );
 *     tpl.overwrite(panel.body, data.kids); 
 *
 * An example illustrating how the **for** property can be leveraged to access specified members of the provided data
 * object to populate the template:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Title: {title}</p>',
 *         '<p>Company: {company}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',     
 *             '<p>{name}</p>',
 *         '</tpl></p>'
 *     );
 *     tpl.overwrite(panel.body, data);  
 *
 * Flat arrays that contain values (and not objects) can be auto-rendered using the special **`{.}`** variable inside a
 * loop. This variable will represent the value of the array at the current index:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>{name}\'s favorite beverages:</p>',
 *         '<tpl for="drinks">',
 *             '<div> - {.}</div>',
 *         '</tpl>'
 *     );
 *     tpl.overwrite(panel.body, data);
 *
 * When processing a sub-template, for example while looping through a child array, you can access the parent object's
 * members via the **parent** object:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<tpl if="age &gt; 1">',
 *                 '<p>{name}</p>',
 *                 '<p>Dad: {parent.name}</p>',
 *             '</tpl>',
 *         '</tpl></p>'
 *     );
 *     tpl.overwrite(panel.body, data);
 *     
 * The **foreach** operator is used to loop over an object's properties.  The following
 * example demonstrates looping over the main data object's properties:
 * 
 *     var tpl = new Ext.XTemplate(
 *         '<dl>',
 *             '<tpl foreach=".">',
 *                 '<dt>{$}</dt>', // the special **`{$}`** variable contains the property name
 *                 '<dd>{.}</dd>', // within the loop, the **`{.}`** variable is set to the property value
 *             '</tpl>',
 *         '</dl>'
 *     );
 *     tpl.overwrite(panel.body, data);
 *
 * # Conditional processing with basic comparison operators
 *
 * The **tpl** tag and the **if** operator are used to provide conditional checks for deciding whether or not to render
 * specific parts of the template.
 *
 * Using the sample data above:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<tpl if="age &gt; 1">',
 *                 '<p>{name}</p>',
 *             '</tpl>',
 *         '</tpl></p>'
 *     );
 *     tpl.overwrite(panel.body, data);
 *
 * More advanced conditionals are also supported:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<p>{name} is a ',
 *             '<tpl if="age &gt;= 13">',
 *                 '<p>teenager</p>',
 *             '<tpl elseif="age &gt;= 2">',
 *                 '<p>kid</p>',
 *             '<tpl else>',
 *                 '<p>baby</p>',
 *             '</tpl>',
 *         '</tpl></p>'
 *     );
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<p>{name} is a ',
 *             '<tpl switch="name">',
 *                 '<tpl case="Aubrey" case="Nikol">',
 *                     '<p>girl</p>',
 *                 '<tpl default>',
 *                     '<p>boy</p>',
 *             '</tpl>',
 *         '</tpl></p>'
 *     );
 *
 * A `break` is implied between each case and default, however, multiple cases can be listed
 * in a single &lt;tpl&gt; tag.
 *
 * # Using double quotes
 *
 * Examples:
 *
 *     var tpl = new Ext.XTemplate(
 *         "<tpl if='age &gt; 1 && age &lt; 10'>Child</tpl>",
 *         "<tpl if='age &gt;= 10 && age &lt; 18'>Teenager</tpl>",
 *         "<tpl if='this.isGirl(name)'>...</tpl>",
 *         '<tpl if="id == \'download\'">...</tpl>',
 *         "<tpl if='needsIcon'><img src='{icon}' class='{iconCls}'/></tpl>",
 *         "<tpl if='name == \"Don\"'>Hello</tpl>"
 *     );
 *
 * # Basic math support
 *
 * The following basic math operators may be applied directly on numeric data values:
 *
 *     + - * /
 *
 * For example:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<tpl if="age &gt; 1">',  
 *                 '<p>{#}: {name}</p>',  
 *                 '<p>In 5 Years: {age+5}</p>',  
 *                 '<p>Dad: {parent.name}</p>',
 *             '</tpl>',
 *         '</tpl></p>'
 *     );
 *     tpl.overwrite(panel.body, data);
 *
 * # Execute arbitrary inline code with special built-in template variables
 *
 * Anything between `{[ ... ]}` is considered code to be executed in the scope of the template.
 * The expression is evaluated and the result is included in the generated result. There are
 * some special variables available in that code:
 *
 * - **out**: The output array into which the template is being appended (using `push` to later
 *   `join`).
 * - **values**: The values in the current scope. If you are using scope changing sub-templates,
 *   you can change what values is.
 * - **parent**: The scope (values) of the ancestor template.
 * - **xindex**: If you are in a "for" or "foreach" looping template, the index of the loop you are in (1-based).
 * - **xcount**: If you are in a "for" looping template, the total length of the array you are looping.
 * - **xkey**: If you are in a "foreach" looping template, the key of the current property
 * being examined.
 *
 * This example demonstrates basic row striping using an inline code block and the xindex variable:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Company: {[values.company.toUpperCase() + ", " + values.title]}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
 *             '{name}',
 *             '</div>',
 *         '</tpl></p>'
 *      );
 *
 * Any code contained in "verbatim" blocks (using "{% ... %}") will be inserted directly in
 * the generated code for the template. These blocks are not included in the output. This
 * can be used for simple things like break/continue in a loop, or control structures or
 * method calls (when they don't produce output). The `this` references the template instance.
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Company: {[values.company.toUpperCase() + ", " + values.title]}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '{% if (xindex % 2 === 0) continue; %}',
 *             '{name}',
 *             '{% if (xindex > 100) break; %}',
 *             '</div>',
 *         '</tpl></p>'
 *      );
 *
 * # Template member functions
 *
 * One or more member functions can be specified in a configuration object passed into the XTemplate constructor for
 * more complex processing:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<tpl if="this.isGirl(name)">',
 *                 '<p>Girl: {name} - {age}</p>',
 *             '<tpl else>',
 *                 '<p>Boy: {name} - {age}</p>',
 *             '</tpl>',
 *             '<tpl if="this.isBaby(age)">',
 *                 '<p>{name} is a baby!</p>',
 *             '</tpl>',
 *         '</tpl></p>',
 *         {
 *             // XTemplate configuration:
 *             disableFormats: true,
 *             // member functions:
 *             isGirl: function(name){
 *                return name == 'Aubrey' || name == 'Nikol';
 *             },
 *             isBaby: function(age){
 *                return age < 1;
 *             }
 *         }
 *     );
 *     tpl.overwrite(panel.body, data);
 */
Ext.define('Ext.XTemplate', {
    extend:  Ext.Template ,

                                      

    /**
     * @private
     */
    emptyObj: {},

    /**
     * @cfg {Boolean} compiled
     * Only applies to {@link Ext.Template}, XTemplates are compiled automatically on the
     * first call to {@link #apply} or {@link #applyOut}.
     * @hide
     */

    /**
     * @cfg {String/Array} definitions
     * Optional. A statement, or array of statements which set up `var`s which may then
     * be accessed within the scope of the generated function.
     */

    apply: function(values, parent) {
        return this.applyOut(values, [], parent).join('');
    },

    applyOut: function(values, out, parent) {
        var me = this,
            compiler;

        if (!me.fn) {
            compiler = new Ext.XTemplateCompiler({
                useFormat: me.disableFormats !== true,
                definitions: me.definitions
            });

            me.fn = compiler.compile(me.html);
        }

        try {
            me.fn(out, values, parent || me.emptyObj, 1, 1);
        } catch (e) {
        }

        return out;
    },

    /**
     * Does nothing. XTemplates are compiled automatically, so this function simply returns this.
     * @return {Ext.XTemplate} this
     */
    compile: function() {
        return this;
    },

    statics: {
        /**
         * Gets an `XTemplate` from an object (an instance of an {@link Ext#define}'d class).
         * Many times, templates are configured high in the class hierarchy and are to be
         * shared by all classes that derive from that base. To further complicate matters,
         * these templates are seldom actual instances but are rather configurations. For
         * example:
         *
         *      Ext.define('MyApp.Class', {
         *          extraCls: 'extra-class',
         *
         *          someTpl: [
         *              '<div class="{%this.emitClass(out)%}"></div>',
         *          {
         *              
         *              emitClass: function(out) {
         *                  out.push(this.owner.extraCls);
         *              }
         *          }]
         *      });
         *
         * The goal being to share that template definition with all instances and even
         * instances of derived classes, until `someTpl` is overridden. This method will
         * "upgrade" these configurations to be real `XTemplate` instances *in place* (to
         * avoid creating one instance per object).
         *
         * The resulting XTemplate will have an `owner` reference injected which refers back
         * to the owning object whether that is an object which has an *own instance*, or a
         * class prototype. Through this link, XTemplate member functions will be able to access
         * prototype properties of its owning class.
         *
         * @param {Object} instance The object from which to get the `XTemplate` (must be
         * an instance of an {@link Ext#define}'d class).
         * @param {String} name The name of the property by which to get the `XTemplate`.
         * @return {Ext.XTemplate} The `XTemplate` instance or null if not found.
         * @protected
         * @static
         */
        getTpl: function (instance, name) {
            var tpl = instance[name], // go for it! 99% of the time we will get it!
                owner;

            if (tpl && !tpl.isTemplate) { // tpl is just a configuration (not an instance)
                // create the template instance from the configuration:
                tpl = Ext.ClassManager.dynInstantiate('Ext.XTemplate', tpl);

                // and replace the reference with the new instance:
                if (instance.hasOwnProperty(name)) { // the tpl is on the instance
                    owner = instance;
                } else { // must be somewhere in the prototype chain
                    for (owner = instance.self.prototype; owner && !owner.hasOwnProperty(name); owner = owner.superclass) {
                    }
                }
                owner[name] = tpl;
                tpl.owner = owner;
            }
            // else !tpl (no such tpl) or the tpl is an instance already... either way, tpl
            // is ready to return

            return tpl || null;
        }
    }
});

// @tag dom,core
// @require Helper.js
// @define Ext.dom.Query
// @define Ext.core.DomQuery
// @define Ext.DomQuery

/*
 * This is code is also distributed under MIT license for use
 * with jQuery and prototype JavaScript libraries.
 */
/**
 * @class Ext.dom.Query
 * @alternateClassName Ext.DomQuery
 * @alternateClassName Ext.core.DomQuery
 * @singleton
 *
 * Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes
 * and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
 *
 * DomQuery supports most of the [CSS3 selectors spec][1], along with some custom selectors and basic XPath.
 *
 * All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example
 * `div.foo:nth-child(odd)[@foo=bar].bar:first` would be a perfectly valid selector. Node filters are processed
 * in the order in which they appear, which allows you to optimize your queries for your document structure.
 * 
 * ## Simple Selectors
 * 
 * For performance reasons, some query methods accept selectors that are termed as **simple selectors**. A simple
 * selector is a selector that does not include contextual information about any parent/sibling elements.
 * 
 * Some examples of valid simple selectors:
 * 
 *     var simple = '.foo'; // Only asking for the class name on the element
 *     var simple = 'div.bar'; // Only asking for the tag/class name on the element
 *     var simple = '[href];' // Asking for an attribute on the element.
 *     var simple = ':not(.foo)'; // Only asking for the non-matches against the class name
 *     var simple = 'span:first-child'; // Doesn't require any contextual information about the parent node
 * 
 * Simple examples of invalid simple selectors:
 * 
 *     var notSimple = 'div.foo div.bar'; 
 *     var notSimple = 'span + div'; 
 *
 * ## Element Selectors:
 *
 *   - **`*`** any element
 *   - **`E`** an element with the tag E
 *   - **`E F`** All descendent elements of E that have the tag F
 *   - **`E > F`** or **E/F** all direct children elements of E that have the tag F
 *   - **`E + F`** all elements with the tag F that are immediately preceded by an element with the tag E
 *   - **`E ~ F`** all elements with the tag F that are preceded by a sibling element with the tag E
 *
 * ## Attribute Selectors:
 *
 * The use of `@` and quotes are optional. For example, `div[@foo='bar']` is also a valid attribute selector.
 *
 *   - **`E[foo]`** has an attribute "foo"
 *   - **`E[foo=bar]`** has an attribute "foo" that equals "bar"
 *   - **`E[foo^=bar]`** has an attribute "foo" that starts with "bar"
 *   - **`E[foo$=bar]`** has an attribute "foo" that ends with "bar"
 *   - **`E[foo*=bar]`** has an attribute "foo" that contains the substring "bar"
 *   - **`E[foo%=2]`** has an attribute "foo" that is evenly divisible by 2
 *   - **`E[foo!=bar]`** attribute "foo" does not equal "bar"
 *
 * ## Pseudo Classes:
 *
 *   - **`E:first-child`** E is the first child of its parent
 *   - **`E:last-child`** E is the last child of its parent
 *   - **`E:nth-child(_n_)`** E is the _n_th child of its parent (1 based as per the spec)
 *   - **`E:nth-child(odd)`** E is an odd child of its parent
 *   - **`E:nth-child(even)`** E is an even child of its parent
 *   - **`E:only-child`** E is the only child of its parent
 *   - **`E:checked`** E is an element that is has a checked attribute that is true (e.g. a radio or checkbox)
 *   - **`E:first`** the first E in the resultset
 *   - **`E:last`** the last E in the resultset
 *   - **`E:nth(_n_)`** the _n_th E in the resultset (1 based)
 *   - **`E:odd`** shortcut for :nth-child(odd)
 *   - **`E:even`** shortcut for :nth-child(even)
 *   - **`E:contains(foo)`** E's innerHTML contains the substring "foo"
 *   - **`E:nodeValue(foo)`** E contains a textNode with a nodeValue that equals "foo"
 *   - **`E:not(S)`** an E element that does not match simple selector S
 *   - **`E:has(S)`** an E element that has a descendent that matches simple selector S
 *   - **`E:next(S)`** an E element whose next sibling matches simple selector S
 *   - **`E:prev(S)`** an E element whose previous sibling matches simple selector S
 *   - **`E:any(S1|S2|S2)`** an E element which matches any of the simple selectors S1, S2 or S3
 *   - **`E:visible(true)`** an E element which is deeply visible according to {@link Ext.dom.Element#isVisible}
 *
 * ## CSS Value Selectors:
 *
 *   - **`E{display=none}`** css value "display" that equals "none"
 *   - **`E{display^=none}`** css value "display" that starts with "none"
 *   - **`E{display$=none}`** css value "display" that ends with "none"
 *   - **`E{display*=none}`** css value "display" that contains the substring "none"
 *   - **`E{display%=2}`** css value "display" that is evenly divisible by 2
 *   - **`E{display!=none}`** css value "display" that does not equal "none"
 * 
 * ## XML Namespaces:
 *   - **`ns|E`** an element with tag E and namespace prefix ns
 *
 * [1]: http://www.w3.org/TR/2005/WD-css3-selectors-20051215/#selectors
 */
Ext.ns('Ext.core');

Ext.dom.Query = Ext.core.DomQuery = Ext.DomQuery = (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*#/,
        // This is for IE MSXML which does not support expandos.
        // IE runs the same speed using setAttribute, however FF slows way down
        // and Safari completely fails so they need to continue to use expandos.
        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,
        // True if the browser supports the following syntax:
        // document.getElementsByTagName('namespacePrefix:tagName')
        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;
        })(),

        // replaces a long hex regex match group with the appropriate ascii value
        // $args indicate regex match pos
        longHexToChar = function($0, $1) {
            return String.fromCharCode(parseInt($1, 16));
        },

        // converts a shortHex regex match to the long form
        shortToLongHex = function($0, $1) {
            while ($1.length < 6) {
                $1 = '0' + $1;
            }
            return '\\' + $1;
        },

        // converts a single char escape to long escape form
        charToLongHex = function($0, $1) {
            num = $1.charCodeAt(0).toString(16);
            if (num.length === 1) {
                num = '0' + num;
            }
            return '\\0000' + num;
        },

        // Un-escapes an input selector string.  Assumes all escape sequences have been
        // normalized to the css '\\0000##' 6-hex-digit style escape sequence :
        // will not handle any other escape formats
        unescapeCssSelector = function(selector) {
            return (hasEscapes) ? selector.replace(longHex, longHexToChar) : selector;
        },

        // checks if the path has escaping & does any appropriate replacements
        setupEscapes = function(path) {
            hasEscapes = (path.indexOf('\\') > -1);
            if (hasEscapes) {
                path = path
                    .replace(shortHex, shortToLongHex)
                    .replace(nonHex, charToLongHex)
                    .replace(escapes, '\\\\');  // double the '\' for js compilation
            }
            return path;
        };

    // this eval is stop the compressor from
    // renaming the variable to something shorter
    eval("var batch = 30803, child, next, prev, byClassName;");

    // Retrieve the child node from a particular
    // parent at the specified index.
    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;
        };

    // retrieve the next element node
    next = useElementPointer ?
        function(n) {
            return n.nextElementSibling;
        } :
        function(n) {
            while ((n = n.nextSibling) && n.nodeType != 1);
            return n;
        };

    // retrieve the previous element node
    prev = useElementPointer ?
        function(n) {
            return n.previousElementSibling;
        } :
        function(n) {
            while ((n = n.previousSibling) && n.nodeType != 1);
            return n;
        };

    // Mark each child node with a nodeIndex skipping and
    // removing empty text nodes.
    function children(parent) {
        var n = parent.firstChild,
            nodeIndex = -1,
            nextNode;

        while (n) {
            nextNode = n.nextSibling;
            // clean worthless empty nodes.
            if (n.nodeType == 3 && !nonSpace.test(n.nodeValue)) {
                parent.removeChild(n);
            } else {
                // add an expando nodeIndex
                n.nodeIndex = ++nodeIndex;
            }
            n = nextNode;
        }
        return this;
    }

    // nodeSet - array of nodes
    // cls - CSS Class
    byClassName = useClassList ? // Use classList API where available: http://jsperf.com/classlist-vs-old-school-check/
        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) {
                    // Some elements types (SVG) may not always have a classList
                    // in some browsers, so fallback to the old style here
                    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 its an array, use the first node.
        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];

    }

    // ns - nodes
    // mode - false, /, >, +, ~
    // tagName - defaults to "*"
    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('|', ':') || "*";
        // convert to array
        if (typeof ns.getElementsByTagName != "undefined") {
            ns = [ns];
        }

        // no mode specified, grab all elements by tagName
        // at any depth
        if (!mode) {
            tagName = unescapeCssSelector(tagName);
            if (!supportsColonNsSeparator && DQ.isXml(ns[0]) &&
                tagName.indexOf(':') !== -1) {
                // Some browsers (e.g. WebKit and Opera do not support the following syntax
                // in xml documents: getElementsByTagName('ns:tagName'). To work around
                // this, we remove the namespace prefix from the tagName, get the elements
                // by tag name only, and then compare each element's tagName property to
                
                
                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 DQ = {
        getStyle: function(el, name) {
            return Ext.fly(el, '_DomQuery').getStyle(name);
        },
        
        compile: function(path, type) {
            type = type || "select";

            
            var fn = ["var f = function(root) {\n var mode; ++batch; var n = root || document;\n"],
                lastPath,
                matchers = DQ.matchers,
                matchersLn = matchers.length,
                modeMatch,
                
                lmode = path.match(modeRe),
                tokenMatch, matched, j, t, m;

            path = setupEscapes(path);

            if (lmode && lmode[1]) {
                fn[fn.length] = 'mode="' + lmode[1].replace(trimRe, "") + '";';
                path = path.replace(lmode[1], "");
            }

            
            while (path.substr(0, 1) == "/") {
                path = path.substr(1);
            }

            while (path && lastPath != path) {
                lastPath = path;
                tokenMatch = path.match(tagTokenRe);
                if (type == "select") {
                    if (tokenMatch) {
                        
                        if (tokenMatch[1] == "#") {
                            fn[fn.length] = 'n = quickId(n, mode, root, "' + tokenMatch[2] + '");';
                        } else {
                            fn[fn.length] = 'n = getNodes(n, mode, "' + tokenMatch[2] + '");';
                        }
                        path = path.replace(tokenMatch[0], "");
                    } else if (path.substr(0, 1) != '@') {
                        fn[fn.length] = 'n = getNodes(n, mode, "*");';
                    }
                    
                } else {
                    if (tokenMatch) {
                        if (tokenMatch[1] == "#") {
                            fn[fn.length] = 'n = byId(n, "' + tokenMatch[2] + '");';
                        } else {
                            fn[fn.length] = 'n = byTag(n, "' + tokenMatch[2] + '");';
                        }
                        path = path.replace(tokenMatch[0], "");
                    }
                }
                while (!(modeMatch = path.match(modeRe))) {
                    matched = false;
                    for (j = 0; j < matchersLn; j++) {
                        t = matchers[j];
                        m = path.match(t.re);
                        if (m) {
                            fn[fn.length] = t.select.replace(tplRe, function(x, i) {
                                return m[i];
                            });
                            path = path.replace(m[0], "");
                            matched = true;
                            break;
                        }
                    }
                    
                    if (!matched) {
                        Ext.Error.raise({
                            sourceClass:'Ext.DomQuery',
                            sourceMethod:'compile',
                            msg:'Error parsing selector. Parsing failed at "' + path + '"'
                        });
                    }
                }
                if (modeMatch[1]) {
                    fn[fn.length] = 'mode="' + modeMatch[1].replace(trimRe, "") + '";';
                    path = path.replace(modeMatch[1], "");
                }
            }
            
            fn[fn.length] = "return nodup(n);\n}";

            
            eval(fn.join(""));
            return f;
        },

        
        jsSelect: function(path, root, type) {
            
            root = root || doc;

            if (typeof root == "string") {
                root = doc.getElementById(root);
            }
            var paths = path.split(","),
                results = [],
                i, len, subPath, result;

            
            for (i = 0, len = paths.length; i < len; i++) {
                subPath = paths[i].replace(trimRe, "");
                
                if (!cache[subPath]) {
                    
                    cache[subPath] = DQ.compile(subPath, type);
                    if (!cache[subPath]) {
                        Ext.Error.raise({
                            sourceClass:'Ext.DomQuery',
                            sourceMethod:'jsSelect',
                            msg:subPath + ' is not a valid selector'
                        });
                    }
                } else {
                    
                    
                    setupEscapes(subPath);
                }
                result = cache[subPath](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.escapeId(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, "");
            if (!valueCache[path]) {
                valueCache[path] = DQ.compile(path, "select");
            } else {
                setupEscapes(path);
            }

            var n = valueCache[path](root),
                v;

            n = n[0] ? n[0] : n;

            
            
            
            
            if (typeof n.normalize == 'function') {
                n.normalize();
            }

            v = (n && n.firstChild ? n.firstChild.nodeValue : null);
            return ((v === null || v === undefined || v === '') ? defaultValue : v);
        },

        
        selectNumber: function(path, root, defaultValue) {
            var v = DQ.selectValue(path, root, defaultValue || 0);
            return parseFloat(v);
        },

        
        is: function(el, ss) {
            if (typeof el == "string") {
                el = doc.getElementById(el);
            }
            var isArray = Ext.isArray(el),
                result = DQ.filter(isArray ? el : [el], ss);
            return isArray ? (result.length == el.length) : (result.length > 0);
        },

        
        filter: function(els, ss, nonMatches) {
            ss = ss.replace(trimRe, "");
            if (!simpleCache[ss]) {
                simpleCache[ss] = DQ.compile(ss, "simple");
            } else {
                setupEscapes(ss);
            }

            var result = simpleCache[ss](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}")}};'
        }],

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

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


Ext.query = Ext.DomQuery.select;





Ext.define('Ext.dom.Element_anim', {
    override: 'Ext.dom.Element',

    
    animate: function(config) {
        var me = this,
            animId = me.dom.id || Ext.id(me.dom),
            listeners,
            anim,
            end;
            

        if (!Ext.fx.Manager.hasFxBlock(animId)) {
            
            if (config.listeners) {
                listeners = config.listeners;
                delete config.listeners;
            }
            if (config.internalListeners) {
                config.listeners = config.internalListeners;
                delete config.internalListeners;
            }
            end = config.autoEnd;
            delete config.autoEnd;
            anim = new Ext.fx.Anim(me.anim(config));
            if (listeners) {
                anim.on(listeners);
            }
            Ext.fx.Manager.queueFx(anim);
            if (end) {
                anim.jumpToEnd();
            }
        }
        return me;
    },

    
    anim: function(config) {
        if (!Ext.isObject(config)) {
            return (config) ? {} : false;
        }

        var me = this,
            duration = config.duration || Ext.fx.Anim.prototype.duration,
            easing = config.easing || 'ease',
            animConfig;

        if (config.stopAnimation) {
            me.stopAnimation();
        }

        Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id));

        
        Ext.fx.Manager.setFxDefaults(me.id, {
            delay: 0
        });

        animConfig = {
            
            target: me.dom,
            remove: config.remove,
            alternate: config.alternate || false,
            duration: duration,
            easing: easing,
            callback: config.callback,
            listeners: config.listeners,
            iterations: config.iterations || 1,
            scope: config.scope,
            block: config.block,
            concurrent: config.concurrent,
            delay: config.delay || 0,
            paused: true,
            keyframes: config.keyframes,
            from: config.from || {},
            to: Ext.apply({}, config)
        };
        Ext.apply(animConfig.to, config.to);

        
        delete animConfig.to.to;
        delete animConfig.to.from;
        delete animConfig.to.remove;
        delete animConfig.to.alternate;
        delete animConfig.to.keyframes;
        delete animConfig.to.iterations;
        delete animConfig.to.listeners;
        delete animConfig.to.target;
        delete animConfig.to.paused;
        delete animConfig.to.callback;
        delete animConfig.to.scope;
        delete animConfig.to.duration;
        delete animConfig.to.easing;
        delete animConfig.to.concurrent;
        delete animConfig.to.block;
        delete animConfig.to.stopAnimation;
        delete animConfig.to.delay;
        return animConfig;
    },

    
    slideIn: function(anchor, obj, slideOut) {
        var me = this,
            dom = me.dom,
            elStyle = dom.style,
            beforeAnim,
            wrapAnim,
            restoreScroll,
            wrapDomParentNode;

        anchor = anchor || "t";
        obj = obj || {};

        beforeAnim = function() {
            var animScope = this,
                listeners = obj.listeners,
                el = Ext.fly(dom, '_anim'),
                box, originalStyles, anim, wrap;

            if (!slideOut) {
                el.fixDisplay();
            }

            box = el.getBox();
            if ((anchor == 't' || anchor == 'b') && box.height === 0) {
                box.height = dom.scrollHeight;
            }
            else if ((anchor == 'l' || anchor == 'r') && box.width === 0) {
                box.width = dom.scrollWidth;
            }

            originalStyles = el.getStyles('width', 'height', 'left', 'right', 'top', 'bottom', 'position', 'z-index', true);
            el.setSize(box.width, box.height);

            
            if (obj.preserveScroll) {
                restoreScroll = el.cacheScrollValues();
            }

            wrap = el.wrap({
                role: 'presentation',
                id: Ext.id() + '-anim-wrap-for-' + el.dom.id,
                style: {
                    visibility: slideOut ? 'visible' : 'hidden'
                }
            });
            wrapDomParentNode = wrap.dom.parentNode;
            wrap.setPositioning(el.getPositioning(true));
            if (wrap.isStyle('position', 'static')) {
                wrap.position('relative');
            }
            el.clearPositioning('auto');
            wrap.clip();

            
            if (restoreScroll) {
                restoreScroll();
            }

            
            
            
            el.setStyle({
                visibility: '',
                position: 'absolute'
            });
            if (slideOut) {
                wrap.setSize(box.width, box.height);
            }

            switch (anchor) {
                case 't':
                    anim = {
                        from: {
                            width: box.width + 'px',
                            height: '0px'
                        },
                        to: {
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    elStyle.bottom = '0px';
                    break;
                case 'l':
                    anim = {
                        from: {
                            width: '0px',
                            height: box.height + 'px'
                        },
                        to: {
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    me.anchorAnimX(anchor);
                    break;
                case 'r':
                    anim = {
                        from: {
                            x: box.x + box.width,
                            width: '0px',
                            height: box.height + 'px'
                        },
                        to: {
                            x: box.x,
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    me.anchorAnimX(anchor);
                    break;
                case 'b':
                    anim = {
                        from: {
                            y: box.y + box.height,
                            width: box.width + 'px',
                            height: '0px'
                        },
                        to: {
                            y: box.y,
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    break;
                case 'tl':
                    anim = {
                        from: {
                            x: box.x,
                            y: box.y,
                            width: '0px',
                            height: '0px'
                        },
                        to: {
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    elStyle.bottom = '0px';
                    me.anchorAnimX('l');
                    break;
                case 'bl':
                    anim = {
                        from: {
                            y: box.y + box.height,
                            width: '0px',
                            height: '0px'
                        },
                        to: {
                            y: box.y,
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    me.anchorAnimX('l');
                    break;
                case 'br':
                    anim = {
                        from: {
                            x: box.x + box.width,
                            y: box.y + box.height,
                            width: '0px',
                            height: '0px'
                        },
                        to: {
                            x: box.x,
                            y: box.y,
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    me.anchorAnimX('r');
                    break;
                case 'tr':
                    anim = {
                        from: {
                            x: box.x + box.width,
                            width: '0px',
                            height: '0px'
                        },
                        to: {
                            x: box.x,
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    elStyle.bottom = '0px';
                    me.anchorAnimX('r');
                    break;
            }

            wrap.show();
            wrapAnim = Ext.apply({}, obj);
            delete wrapAnim.listeners;
            wrapAnim = new Ext.fx.Anim(Ext.applyIf(wrapAnim, {
                target: wrap,
                duration: 500,
                easing: 'ease-out',
                from: slideOut ? anim.to : anim.from,
                to: slideOut ? anim.from : anim.to
            }));

            
            wrapAnim.on('afteranimate', function() {
                var el = Ext.fly(dom, '_anim');
                
                el.setStyle(originalStyles);
                if (slideOut) {
                    if (obj.useDisplay) {
                        el.setDisplayed(false);
                    } else {
                        el.hide();
                    }
                }
                if (wrap.dom) {
                    if (wrap.dom.parentNode) {
                        wrap.dom.parentNode.insertBefore(el.dom, wrap.dom);
                    } else {
                        wrapDomParentNode.appendChild(el.dom);
                    }
                    wrap.remove();
                }
                
                if (restoreScroll) {
                    restoreScroll();
                }
                
                animScope.end();
            });
            
            if (listeners) {
                wrapAnim.on(listeners);
            }
        };

        me.animate({
            
            duration: obj.duration ? Math.max(obj.duration, 500) * 2 : 1000,
            listeners: {
                beforeanimate: beforeAnim 
            }
        });
        return me;
    },


    
    slideOut: function(anchor, o) {
        return this.slideIn(anchor, o, true);
    },

    
    puff: function(obj) {
        var me = this,
            dom = me.dom,
            beforeAnim,
            box = me.getBox(),
            originalStyles = me.getStyles('width', 'height', 'left', 'right', 'top', 'bottom', 'position', 'z-index', 'font-size', 'opacity', true);

       obj = Ext.applyIf(obj || {}, {
            easing: 'ease-out',
            duration: 500,
            useDisplay: false
        });

        beforeAnim = function() {
            var el = Ext.fly(dom, '_anim');
            
            el.clearOpacity();
            el.show();
            this.to = {
                width: box.width * 2,
                height: box.height * 2,
                x: box.x - (box.width / 2),
                y: box.y - (box.height /2),
                opacity: 0,
                fontSize: '200%'
            };
            this.on('afteranimate',function() {
                var el = Ext.fly(dom, '_anim');
                if (el) {
                    if (obj.useDisplay) {
                        el.setDisplayed(false);
                    } else {
                        el.hide();
                    }
                    el.setStyle(originalStyles);
                    Ext.callback(obj.callback, obj.scope);
                }
            });
        };

        me.animate({
            duration: obj.duration,
            easing: obj.easing,
            listeners: {
                beforeanimate: {
                    fn: beforeAnim
                }
            }
        });
        return me;
    },

    
    switchOff: function(obj) {
        var me = this,
            dom = me.dom,
            beforeAnim;

        obj = Ext.applyIf(obj || {}, {
            easing: 'ease-in',
            duration: 500,
            remove: false,
            useDisplay: false
        });

        beforeAnim = function() {
            var el = Ext.fly(dom, '_anim'),
                animScope = this,
                size = el.getSize(),
                xy = el.getXY(),
                keyframe, position;
                
            el.clearOpacity();
            el.clip();
            position = el.getPositioning();

            keyframe = new Ext.fx.Animator({
                target: dom,
                duration: obj.duration,
                easing: obj.easing,
                keyframes: {
                    33: {
                        opacity: 0.3
                    },
                    66: {
                        height: 1,
                        y: xy[1] + size.height / 2
                    },
                    100: {
                        width: 1,
                        x: xy[0] + size.width / 2
                    }
                }
            });
            keyframe.on('afteranimate', function() {
                var el = Ext.fly(dom, '_anim');
                if (obj.useDisplay) {
                    el.setDisplayed(false);
                } else {
                    el.hide();
                }
                el.clearOpacity();
                el.setPositioning(position);
                el.setSize(size);
                
                animScope.end();
            });
        };
        
        me.animate({
            
            duration: (Math.max(obj.duration, 500) * 2),
            listeners: {
                beforeanimate: {
                    fn: beforeAnim
                }
            },
            callback: obj.callback,
            scope: obj.scope
        });
        return me;
    },

    
    frame : function(color, count, obj){
        var me = this,
            dom = me.dom,
            beforeAnim;

        color = color || '#C3DAF9';
        count = count || 1;
        obj = obj || {};

        beforeAnim = function() {
            var el = Ext.fly(dom, '_anim'),
                animScope = this,
                box,
                proxy, proxyAnim;
                
            el.show();
            box = el.getBox();
            proxy = Ext.getBody().createChild({
                role: 'presentation',
                id: el.dom.id + '-anim-proxy',
                style: {
                    position : 'absolute',
                    'pointer-events': 'none',
                    'z-index': 35000,
                    border : '0px solid ' + color
                }
            });
            
            proxyAnim = new Ext.fx.Anim({
                target: proxy,
                duration: obj.duration || 1000,
                iterations: count,
                from: {
                    top: box.y,
                    left: box.x,
                    borderWidth: 0,
                    opacity: 1,
                    height: box.height,
                    width: box.width
                },
                to: {
                    top: box.y - 20,
                    left: box.x - 20,
                    borderWidth: 10,
                    opacity: 0,
                    height: box.height + 40,
                    width: box.width + 40
                }
            });
            proxyAnim.on('afteranimate', function() {
                proxy.remove();
                
                animScope.end();
            });
        };

        me.animate({
            
            duration: (Math.max(obj.duration, 500) * 2) || 2000,
            listeners: {
                beforeanimate: {
                    fn: beforeAnim
                }
            },
            callback: obj.callback,
            scope: obj.scope
        });
        return me;
    },

    
    ghost: function(anchor, obj) {
        var me = this,
            dom = me.dom,
            beforeAnim;

        anchor = anchor || "b";
        beforeAnim = function() {
            var el = Ext.fly(dom, '_anim'),
                width = el.getWidth(),
                height = el.getHeight(),
                xy = el.getXY(),
                position = el.getPositioning(),
                to = {
                    opacity: 0
                };
            switch (anchor) {
                case 't':
                    to.y = xy[1] - height;
                    break;
                case 'l':
                    to.x = xy[0] - width;
                    break;
                case 'r':
                    to.x = xy[0] + width;
                    break;
                case 'b':
                    to.y = xy[1] + height;
                    break;
                case 'tl':
                    to.x = xy[0] - width;
                    to.y = xy[1] - height;
                    break;
                case 'bl':
                    to.x = xy[0] - width;
                    to.y = xy[1] + height;
                    break;
                case 'br':
                    to.x = xy[0] + width;
                    to.y = xy[1] + height;
                    break;
                case 'tr':
                    to.x = xy[0] + width;
                    to.y = xy[1] - height;
                    break;
            }
            this.to = to;
            this.on('afteranimate', function () {
                var el = Ext.fly(dom, '_anim');
                if (el) {
                    el.hide();
                    el.clearOpacity();
                    el.setPositioning(position);
                }
            });
        };

        me.animate(Ext.applyIf(obj || {}, {
            duration: 500,
            easing: 'ease-out',
            listeners: {
                beforeanimate: beforeAnim
            }
        }));
        return me;
    },

    
    highlight: function(color, o) {
        var me = this,
            dom = me.dom,
            from = {},
            restore, to, attr, lns, event, fn;

        
        if (dom.tagName.match(me.tableTagRe)) {
            return me.select('div').highlight(color, o);
        }

        o = o || {};
        lns = o.listeners || {};
        attr = o.attr || 'backgroundColor';
        from[attr] = color || 'ffff9c';

        if (!o.to) {
            to = {};
            to[attr] = o.endColor || me.getColor(attr, 'ffffff', '');
        }
        else {
            to = o.to;
        }

        
        o.listeners = Ext.apply(Ext.apply({}, lns), {
            beforeanimate: function() {
                restore = dom.style[attr];
                var el = Ext.fly(dom, '_anim');
                el.clearOpacity();
                el.show();

                event = lns.beforeanimate;
                if (event) {
                    fn = event.fn || event;
                    return fn.apply(event.scope || lns.scope || window, arguments);
                }
            },
            afteranimate: function() {
                if (dom) {
                    dom.style[attr] = restore;
                }

                event = lns.afteranimate;
                if (event) {
                    fn = event.fn || event;
                    fn.apply(event.scope || lns.scope || window, arguments);
                }
            }
        });

        me.animate(Ext.apply({}, o, {
            duration: 1000,
            easing: 'ease-in',
            from: from,
            to: to
        }));
        return me;
    },

   
    pause: function(ms) {
        var me = this;
        Ext.fx.Manager.setFxDefaults(me.id, {
            delay: ms
        });
        return me;
    },

    
    fadeIn: function(o) {
        var me = this,
            dom = me.dom;
            
        me.animate(Ext.apply({}, o, {
            opacity: 1,
            internalListeners: {
                beforeanimate: function(anim){
                    
                    
                    var el = Ext.fly(dom, '_anim');
                    if (el.isStyle('display', 'none')) {
                        el.setDisplayed('');
                    } else {
                        el.show();
                    } 
                }
            }
        }));
        return this;
    },

    
    fadeOut: function(o) {
        var me = this,
            dom = me.dom;
            
        o = Ext.apply({
            opacity: 0,
            internalListeners: {
                afteranimate: function(anim){
                    if (dom && anim.to.opacity === 0) {
                        var el = Ext.fly(dom, '_anim');
                        if (o.useDisplay) {
                            el.setDisplayed(false);
                        } else {
                            el.hide();
                        }
                    }         
                }
            }
        }, o);
        me.animate(o);
        return me;
    },

    
    scale: function(w, h, o) {
        this.animate(Ext.apply({}, o, {
            width: w,
            height: h
        }));
        return this;
    },

    
    shift: function(config) {
        this.animate(config);
        return this;
    },

    
    anchorAnimX: function(anchor) {
        var xName = (anchor === 'l') ? 'right' : 'left';
        this.dom.style[xName] = '0px';
    }
});



Ext.define('Ext.dom.Element_dd', {
    override: 'Ext.dom.Element',

    
    initDD : function(group, config, overrides){
        var dd = new Ext.dd.DD(Ext.id(this.dom), group, config);
        return Ext.apply(dd, overrides);
    },

    
    initDDProxy : function(group, config, overrides){
        var dd = new Ext.dd.DDProxy(Ext.id(this.dom), group, config);
        return Ext.apply(dd, overrides);
    },

    
    initDDTarget : function(group, config, overrides){
        var dd = new Ext.dd.DDTarget(Ext.id(this.dom), group, config);
        return Ext.apply(dd, overrides);
    }
});



Ext.define('Ext.dom.Element_fx', {
    override: 'Ext.dom.Element'
},
function() {

var Element         = Ext.dom.Element,
    VISIBILITY      = "visibility",
    DISPLAY         = "display",
    NONE            = "none",
    HIDDEN          = 'hidden',
    VISIBLE         = 'visible',
    OFFSETS         = "offsets",
    ASCLASS         = "asclass",
    NOSIZE          = 'nosize',
    ORIGINALDISPLAY = 'originalDisplay',
    VISMODE         = 'visibilityMode',
    ISVISIBLE       = 'isVisible',
    OFFSETCLASS     = Ext.baseCSSPrefix + 'hide-offsets',
    getDisplay = function(el) {
        var data = (el.$cache || el.getCache()).data,
            display = data[ORIGINALDISPLAY];
            
        if (display === undefined) {
            data[ORIGINALDISPLAY] = display = '';
        }
        return display;
    },
    getVisMode = function(el){
        var data = (el.$cache || el.getCache()).data,
            visMode = data[VISMODE];
            
        if (visMode === undefined) {
            data[VISMODE] = visMode = Element.VISIBILITY;
        }
        return visMode;
    };

Element.override({
    
    originalDisplay : "",
    visibilityMode : 1,

    
    setVisible : function(visible, animate) {
        var me = this,
            dom = me.dom,
            visMode = getVisMode(me);

        
        if (typeof animate == 'string') {
            switch (animate) {
                case DISPLAY:
                    visMode = Element.DISPLAY;
                    break;
                case VISIBILITY:
                    visMode = Element.VISIBILITY;
                    break;
                case OFFSETS:
                    visMode = Element.OFFSETS;
                    break;
                case NOSIZE:
                case ASCLASS:
                    visMode = Element.ASCLASS;
                    break;
            }
            me.setVisibilityMode(visMode);
            animate = false;
        }

        if (!animate || !me.anim) {
            if (visMode == Element.DISPLAY) {
                return me.setDisplayed(visible);
            } else if (visMode == Element.OFFSETS) {
                me[visible?'removeCls':'addCls'](OFFSETCLASS);
            } else if (visMode == Element.VISIBILITY) {
                me.fixDisplay();
                
                dom.style.visibility = visible ? '' : HIDDEN;
            } else if (visMode == Element.ASCLASS) {
                me[visible?'removeCls':'addCls'](me.visibilityCls || Element.visibilityCls);
            }
        } else {
            
            if (visible) {
                me.setOpacity(0.01);
                me.setVisible(true);
            }
            if (!Ext.isObject(animate)) {
                animate = {
                    duration: 350,
                    easing: 'ease-in'
                };
            }
            me.animate(Ext.applyIf({
                callback: function() {
                    if (!visible) {
                        
                        
                        Ext.fly(dom, '_internal').setVisible(false).setOpacity(1);
                    }
                },
                to: {
                    opacity: (visible) ? 1 : 0
                }
            }, animate));
        }
        (me.$cache || me.getCache()).data[ISVISIBLE] = visible;
        return me;
    },

    
    hasMetrics  : function(){
        var visMode = getVisMode(this);
        return this.isVisible() || (visMode == Element.OFFSETS) || (visMode == Element.VISIBILITY);
    },

    
    toggle : function(animate){
        var me = this;
        me.setVisible(!me.isVisible(), me.anim(animate));
        return me;
    },

    
    setDisplayed : function(value) {
        if(typeof value == "boolean"){
           value = value ? getDisplay(this) : NONE;
        }
        this.setStyle(DISPLAY, value);
        return this;
    },

    
    fixDisplay : function(){
        var me = this;
        if (me.isStyle(DISPLAY, NONE)) {
            me.setStyle(VISIBILITY, HIDDEN);
            me.setStyle(DISPLAY, getDisplay(me)); 
            if (me.isStyle(DISPLAY, NONE)) { 
                me.setStyle(DISPLAY, "block");
            }
        }
    },

    
    hide : function(animate){
        
        if (typeof animate == 'string'){
            this.setVisible(false, animate);
            return this;
        }
        this.setVisible(false, this.anim(animate));
        return this;
    },

    
    show : function(animate){
        
        if (typeof animate == 'string'){
            this.setVisible(true, animate);
            return this;
        }
        this.setVisible(true, this.anim(animate));
        return this;
    }
});

});



Ext.define('Ext.dom.Element_position', {
    override: 'Ext.dom.Element'
},
function() {

var flyInstance,
    Element = this,
    LEFT = "left",
    RIGHT = "right",
    TOP = "top",
    BOTTOM = "bottom",
    POSITION = "position",
    STATIC = "static",
    RELATIVE = "relative",
    ZINDEX = "z-index",
    BODY = 'BODY',

    PADDING = 'padding',
    BORDER = 'border',
    SLEFT = '-left',
    SRIGHT = '-right',
    STOP = '-top',
    SBOTTOM = '-bottom',
    SWIDTH = '-width',
    
    borders = {l: BORDER + SLEFT + SWIDTH, r: BORDER + SRIGHT + SWIDTH, t: BORDER + STOP + SWIDTH, b: BORDER + SBOTTOM + SWIDTH},
    paddings = {l: PADDING + SLEFT, r: PADDING + SRIGHT, t: PADDING + STOP, b: PADDING + SBOTTOM},
    paddingsTLRB = [paddings.l, paddings.r, paddings.t, paddings.b],
    bordersTLRB = [borders.l,  borders.r,  borders.t,  borders.b],
    round = Math.round,
    doc = document,
    fly = function (el) {
        if (!flyInstance) {
            flyInstance = new Ext.Element.Fly();
        }
        flyInstance.attach(el);
        return flyInstance;
    };

    Element.override({

        pxRe: /^\d+(?:\.\d*)?px$/i,

        inheritableStatics: {
            getX: function(el) {
                return Element.getXY(el)[0];
            },

            getXY: function(el) {
                var bd = doc.body,
                    docEl = doc.documentElement,
                    leftBorder = 0,
                    topBorder = 0,
                    ret = [0,0],
                    box,
                    scroll;

                el = Ext.getDom(el);

                if(el != doc && el != bd){
                    
                    
                    if (Ext.isIE) {
                        try {
                            box = el.getBoundingClientRect();
                            
                            
                            topBorder = docEl.clientTop || bd.clientTop;
                            leftBorder = docEl.clientLeft || bd.clientLeft;
                        } catch (ex) {
                            box = { left: 0, top: 0 };
                        }
                    } else {
                        box = el.getBoundingClientRect();
                    }

                    scroll = fly(doc).getScroll();
                    ret = [
                        round(box.left + scroll.left - leftBorder),
                        round(box.top + scroll.top - topBorder)
                    ];
                }
                return ret;
            },

            getY: function(el) {
                return Element.getXY(el)[1];
            },

            setX: function(el, x) {
                Element.setXY(el, [x, false]);
            },

            setXY: function(el, xy) {
                (el = Ext.fly(el, '_setXY')).position();

                var pts = el.translatePoints(xy),
                    style = el.dom.style,
                    pos;

                
                
                style.right = 'auto';
                for (pos in pts) {
                    if (!isNaN(pts[pos])) {
                        style[pos] = pts[pos] + "px";
                    }
                }
            },

            setY: function(el, y) {
                Element.setXY(el, [false, y]);
            }
        },

        
        center: function(centerIn){
            return this.alignTo(centerIn || doc, 'c-c');
        },

        
        clearPositioning: function(value) {
            value = value || '';
            return this.setStyle({
                left : value,
                right : value,
                top : value,
                bottom : value,
                'z-index' : '',
                position : STATIC
            });
        },

        getAnchorToXY: function(el, anchor, local, mySize) {
            return el.getAnchorXY(anchor, local, mySize);
        },

        
        getBottom: function(local) {
            return (local ? this.getLocalY() : this.getY()) + this.getHeight();
        },

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

        
        getCenterXY: function(){
            return this.getAlignToXY(doc, 'c-c');
        },

        
        getLeft: function(local) {
            return local ? this.getLocalX() : this.getX();
        },

        
        getLocalX: function() {
            var me = this,
                offsetParent = me.dom.offsetParent,
                x = me.getStyle('left');

            if (!x || x === 'auto') {
                x = 0;
            } else if (me.pxRe.test(x)) {
                x = parseFloat(x);
            } else {
                x = me.getX();
                if (offsetParent) {
                    x -= Element.getX(offsetParent);
                }
            }

            return x;
        },

        
        getLocalXY: function() {
            var me = this,
                offsetParent = me.dom.offsetParent,
                style = me.getStyle(['left', 'top']),
                x = style.left,
                y = style.top;

            if (!x || x === 'auto') {
                x = 0;
            } else if (me.pxRe.test(x)) {
                x = parseFloat(x);
            } else {
                x = me.getX();
                if (offsetParent) {
                    x -= Element.getX(offsetParent);
                }
            }

            if (!y || y === 'auto') {
                y = 0;
            } else if (me.pxRe.test(y)) {
                y = parseFloat(y);
            } else {
                y = me.getY();
                if (offsetParent) {
                    y -= Element.getY(offsetParent);
                }
            }

            return [x, y];
        },

        
        getLocalY: function() {
            var me = this,
                offsetParent = me.dom.offsetParent,
                y = me.getStyle('top');

            if (!y || y === 'auto') {
                y = 0;
            } else if (me.pxRe.test(y)) {
                y = parseFloat(y);
            } else {
                y = me.getY();
                if (offsetParent) {
                    y -= Element.getY(offsetParent);
                }
            }

            return y;
        },

        
        getPageBox: function(getRegion) {
            var me = this,
                dom = me.dom,
                isDoc = dom.nodeName == BODY,
                w = isDoc ? Ext.Element.getViewWidth() : dom.offsetWidth,
                h = isDoc ? Ext.Element.getViewHeight() : 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
                };
            }
        },

        
        getPositioning: function(autoPx){
            var styles = this.getStyle(['left', 'top', 'position', 'z-index']),
                dom = this.dom;

            if(autoPx) {
                if(styles.left === 'auto') {
                    styles.left = dom.offsetLeft + 'px';
                }
                if(styles.top === 'auto') {
                    styles.top = dom.offsetTop + 'px';
                }
            }

            return styles;
        },

        
        getRight: function(local) {
            return (local ? this.getLocalX() : this.getX()) + this.getWidth();
        },

        
        getTop: function(local) {
            return local ? this.getLocalY() : this.getY();
        },

        
        getX: function() {
            return Element.getX(this.dom);
        },

        
        getXY: function() {
            return Element.getXY(this.dom);
        },

        
        getY: function() {
            return Element.getY(this.dom);
        },

        
        moveTo: function(x, y, animate) {
            return this.setXY([x, y], animate);
        },

        
        position: function(pos, zIndex, x, y) {
            var me = this;

            if (!pos && me.isStyle(POSITION, STATIC)) {
                me.setStyle(POSITION, RELATIVE);
            } else if (pos) {
                me.setStyle(POSITION, pos);
            }
            if (zIndex) {
                me.setStyle(ZINDEX, zIndex);
            }
            if (x || y) {
                me.setXY([x || false, y || false]);
            }
        },

        
        setBottom: function(bottom) {
            this.dom.style[BOTTOM] = this.addUnits(bottom);
            return this;
        },

        
        setBounds: function(x, y, width, height, animate) {
            return this.setBox({
                x: x,
                y: y,
                width: width,
                height: height
            }, animate);
        },

        
        setLeft: function(left) {
            this.dom.style[LEFT] = this.addUnits(left); 
            return this;
        },

        
        setLeftTop: function(left, top) {
            var me = this,
                style = me.dom.style;

            style.left = me.addUnits(left);
            style.top = me.addUnits(top);

            return me;
        },

        setLocalX: function(x) {
            var style = this.dom.style;

            
            style.right = 'auto';
            style.left = (x === null) ? 'auto' : x + 'px';
        },

        setLocalXY: function(x, y) {
            var style = this.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';
            }
        },

        setLocalY: function(y) {
            this.dom.style.top = (y === null) ? 'auto' : y + 'px';
        },

        
        setLocation: function(x, y, animate) {
            return this.setXY([x, y], animate);
        },

        
        setPositioning: function(pc) {
            return this.setStyle(pc);
        },

        
        setRight: function(right) {
            this.dom.style[RIGHT] = this.addUnits(right);
            return this;
        },

        
        setTop: function(top) {
            this.dom.style[TOP] = this.addUnits(top);
            return this;
        },

        setX: function(x, animate) {
            return this.setXY([x, this.getY()], animate);
        },

        setXY: function(xy, animate) {
            var me = this;

            if (!animate || !me.anim) {
                Element.setXY(me.dom, xy);
            } else {
                if (!Ext.isObject(animate)) {
                    animate = {};
                }
                me.animate(Ext.applyIf({ to: { x: xy[0], y: xy[1] } }, animate));
            }
            return this;
        },

        setY: function(y, animate) {
            return this.setXY([this.getX(), y], animate);
        }
    });

    
    Element.getTrueXY = Element.getXY;

});



Ext.define('Ext.dom.Element_scroll', {
    override: 'Ext.dom.Element',

    
    isScrollable: function() {
        var dom = this.dom;
        return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
    },

    
    getScroll: function() {
        var me = this,
            dom = me.dom,
            doc = document,
            body = doc.body,
            docElement = doc.documentElement,
            left, top;

        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
        };
    },
    
    
    getScrollLeft: function() {
        var dom = this.dom,
            doc = document;
            
        if (dom === doc || dom === doc.body) {
            return this.getScroll().left;
        } else {
            return dom.scrollLeft;
        }
    },
    
    
    getScrollTop: function(){
        var dom = this.dom,
            doc = document;
            
        if (dom === doc || dom === doc.body) {
            return this.getScroll().top;
        } else {
            return dom.scrollTop;
        }
    },
    
    
    setScrollLeft: function(left){
        this.dom.scrollLeft = left;
        return this;
    },
    
    
    setScrollTop: function(top) {
        this.dom.scrollTop = top;
        return this;
    },

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

    
    scrollTo: function(side, value, animate) {
        
        var top = /top/i.test(side),
            me = this,
            prop = top ? 'scrollTop' : 'scrollLeft',
            dom = me.dom,
            animCfg;

        if (!animate || !me.anim) {
            
            dom[prop] = value;
            
            dom[prop] = value;
        }
        else {
            animCfg = {
                to: {}
            };
            animCfg.to[prop] = value;
            if (Ext.isObject(animate)) {
                Ext.applyIf(animCfg, animate);
            }
            me.animate(animCfg);
        }
        return me;
    },

    
    scrollIntoView: function(container, hscroll, animate, highlight) {
        var me = this,
            dom = me.dom,
            offsets = me.getOffsetsTo(container = Ext.getDom(container) || Ext.getBody().dom),
        
            left = offsets[0] + container.scrollLeft,
            top = offsets[1] + container.scrollTop,
            bottom = top + dom.offsetHeight,
            right = left + dom.offsetWidth,
        
            ctClientHeight = container.clientHeight,
            ctScrollTop = parseInt(container.scrollTop, 10),
            ctScrollLeft = parseInt(container.scrollLeft, 10),
            ctBottom = ctScrollTop + ctClientHeight,
            ctRight = ctScrollLeft + container.clientWidth,
            newPos;

        
        if (highlight) {
            if (animate) {
                animate = Ext.apply({
                    listeners: {
                        afteranimate: function() {
                            me.scrollChildFly.attach(dom).highlight();
                        }
                    }
                }, animate);
            } else {
                me.scrollChildFly.attach(dom).highlight();
            }
        }

        if (dom.offsetHeight > ctClientHeight || top < ctScrollTop) {
            newPos = top;
        } else if (bottom > ctBottom) {
            newPos = bottom - ctClientHeight;
        }
        if (newPos != null) {
            me.scrollChildFly.attach(container).scrollTo('top', newPos, animate);
        }

        if (hscroll !== false) {
            newPos = null;
            if (dom.offsetWidth > container.clientWidth || left < ctScrollLeft) {
                newPos = left;
            } else if (right > ctRight) {
                newPos = right - container.clientWidth;
            }
            if (newPos != null) {
                me.scrollChildFly.attach(container).scrollTo('left', newPos, animate);
            }
        }
        return me;
    },

    
    scrollChildIntoView: function(child, hscroll) {
        this.scrollChildFly.attach(Ext.getDom(child)).scrollIntoView(this, hscroll);
    },

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

    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);
    }
}, function() {
    this.prototype.scrollChildFly = new this.Fly();
    this.prototype.scrolltoFly = new this.Fly();
});



Ext.define('Ext.dom.Element_style', {
    override: 'Ext.dom.Element'
},
function() {

var Element = this,
    view = document.defaultView,
    adjustDirect2DTableRe = /table-row|table-.*-group/,
    INTERNAL = '_internal',
    HIDDEN = 'hidden',
    HEIGHT = 'height',
    WIDTH = 'width',
    ISCLIPPED = 'isClipped',
    OVERFLOW = 'overflow',
    OVERFLOWX = 'overflow-x',
    OVERFLOWY = 'overflow-y',
    ORIGINALCLIP = 'originalClip',
    DOCORBODYRE = /#document|body/i,
    
    
    styleHooks, verticalStyleHooks90, verticalStyleHooks270,
    edges, k, edge, borderWidth;

if (!view || !view.getComputedStyle) {
    Element.prototype.getStyle = function (property, inline) {
        var me = this,
            dom = me.dom,
            multiple = typeof property != 'string',
            hooks = me.styleHooks,
            prop = property,
            props = prop,
            len = 1,
            isInline = inline,
            camel, domStyle, values, hook, out, style, i;

        if (multiple) {
            values = {};
            prop = props[0];
            i = 0;
            if (!(len = props.length)) {
                return values;
            }
        }

        if (!dom || dom.documentElement) {
            return values || '';
        }

        domStyle = dom.style;

        if (inline) {
            style = domStyle;
        } else {
            style = dom.currentStyle;

            
            if (!style) {
                isInline = true;
                style = domStyle;
            }
        }

        do {
            hook = hooks[prop];

            if (!hook) {
                hooks[prop] = hook = { name: Element.normalize(prop) };
            }

            if (hook.get) {
                out = hook.get(dom, me, isInline, style);
            } else {
                camel = hook.name;

                
                
                
                
                if (hook.canThrow) {
                    try {
                        out = style[camel];
                    } catch (e) {
                        out = '';
                    }
                } else {
                    
                    
                    out = style ? style[camel] : '';
                }
            }

            if (!multiple) {
                return out;
            }

            values[prop] = out;
            prop = props[++i];
        } while (i < len);

        return values;
    };
}

Element.override({
    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;
    },

    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.isIE9 && !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;
    },

    setWidth: function(width, animate) {
        var me = this;
        width = me.adjustWidth(width);
        if (!animate || !me.anim) {
            me.dom.style.width = me.addUnits(width);
        }
        else {
            if (!Ext.isObject(animate)) {
                animate = {};
            }
            me.animate(Ext.applyIf({
                to: {
                    width: width
                }
            }, animate));
        }
        return me;
    },

    setHeight : function(height, animate) {
        var me = this;

        height = me.adjustHeight(height);
        if (!animate || !me.anim) {
            me.dom.style.height = me.addUnits(height);
        }
        else {
            if (!Ext.isObject(animate)) {
                animate = {};
            }
            me.animate(Ext.applyIf({
                to: {
                    height: height
                }
            }, animate));
        }

        return me;
    },

    applyStyles: function(style) {
        Ext.DomHelper.applyStyles(this.dom, style);
        return this;
    },

    setSize: function(width, height, animate) {
        var me = this;

        if (Ext.isObject(width)) { 
            animate = height;
            height = width.height;
            width = width.width;
        }

        width = me.adjustWidth(width);
        height = me.adjustHeight(height);

        if (!animate || !me.anim) {
            me.dom.style.width = me.addUnits(width);
            me.dom.style.height = me.addUnits(height);
        }
        else {
            if (animate === true) {
                animate = {};
            }
            me.animate(Ext.applyIf({
                to: {
                    width: width,
                    height: height
                }
            }, animate));
        }

        return me;
    },

    getViewSize : function() {
        var me = this,
            dom = me.dom,
            isDoc = DOCORBODYRE.test(dom.nodeName),
            ret;

        
        if (isDoc) {
            ret = {
                width : Element.getViewWidth(),
                height : Element.getViewHeight()
            };
        } else {
            ret = {
                width : dom.clientWidth,
                height : dom.clientHeight
            };
        }

        return ret;
    },

    getSize: function(contentSize) {
        return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
    },

    

    
    adjustWidth : function(width) {
        var me = this,
            isNum = (typeof width == 'number');

        if (isNum && me.autoBoxAdjust && !me.isBorderBox()) {
            width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
        }
        return (isNum && width < 0) ? 0 : width;
    },

    
    adjustHeight : function(height) {
        var me = this,
            isNum = (typeof height == "number");

        if (isNum && me.autoBoxAdjust && !me.isBorderBox()) {
            height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
        }
        return (isNum && height < 0) ? 0 : height;
    },

    
    getColor : function(attr, defaultValue, prefix) {
        var v = this.getStyle(attr),
            color = prefix || prefix === '' ? prefix : '#',
            h, len, i=0;

        if (!v || (/transparent|inherit/.test(v))) {
            return defaultValue;
        }
        if (/^r/.test(v)) {
             v = v.slice(4, v.length - 1).split(',');
             len = v.length;
             for (; i<len; i++) {
                h = parseInt(v[i], 10);
                color += (h < 16 ? '0' : '') + h.toString(16);
            }
        } else {
            v = v.replace('#', '');
            color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
        }
        return(color.length > 5 ? color.toLowerCase() : defaultValue);
    },

    
    setOpacity: function(opacity, animate) {
        var me = this;

        if (!me.dom) {
            return me;
        }

        if (!animate || !me.anim) {
            me.setStyle('opacity', opacity);
        }
        else {
            if (typeof animate != 'object') {
                animate = {
                    duration: 350,
                    easing: 'ease-in'
                };
            }

            me.animate(Ext.applyIf({
                to: {
                    opacity: opacity
                }
            }, animate));
        }
        return me;
    },

    
    clearOpacity : function() {
        return this.setOpacity('');
    },

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

    
    clip : function() {
        var me = this,
            data = (me.$cache || me.getCache()).data,
            style;

        if (!data[ISCLIPPED]) {
            data[ISCLIPPED] = true;
            style = me.getStyle([OVERFLOW, OVERFLOWX, OVERFLOWY]);
            data[ORIGINALCLIP] = {
                o: style[OVERFLOW],
                x: style[OVERFLOWX],
                y: style[OVERFLOWY]
            };
            me.setStyle(OVERFLOW, HIDDEN);
            me.setStyle(OVERFLOWX, HIDDEN);
            me.setStyle(OVERFLOWY, HIDDEN);
        }
        return me;
    },

    
    unclip : function() {
        var me = this,
            data = (me.$cache || me.getCache()).data,
            clip;

        if (data[ISCLIPPED]) {
            data[ISCLIPPED] = false;
            clip = data[ORIGINALCLIP];
            if (clip.o) {
                me.setStyle(OVERFLOW, clip.o);
            }
            if (clip.x) {
                me.setStyle(OVERFLOWX, clip.x);
            }
            if (clip.y) {
                me.setStyle(OVERFLOWY, clip.y);
            }
        }
        return me;
    },

    
    boxWrap : function(cls) {
        cls = cls || Ext.baseCSSPrefix + 'box';
        var el = Ext.get(this.insertHtml("beforeBegin", "<div class='" + cls + "' role='presentation'>" + Ext.String.format(Element.boxMarkup, cls) + "</div>"));
        Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
        return el;
    },

    
    getComputedHeight : function() {
        var me = this,
            h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
        if (!h) {
            h = parseFloat(me.getStyle(HEIGHT)) || 0;
            if (!me.isBorderBox()) {
                h += me.getFrameWidth('tb');
            }
        }
        return h;
    },

    
    getComputedWidth : function() {
        var me = this,
            w = Math.max(me.dom.offsetWidth, me.dom.clientWidth);

        if (!w) {
            w = parseFloat(me.getStyle(WIDTH)) || 0;
            if (!me.isBorderBox()) {
                w += me.getFrameWidth('lr');
            }
        }
        return w;
    },

    
    getFrameWidth : function(sides, onlyContentBox) {
        return (onlyContentBox && this.isBorderBox()) ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
    },

    
    addClsOnOver : function(className, testFn, scope) {
        var me = this,
            dom = me.dom,
            hasTest = Ext.isFunction(testFn);
            
        me.hover(
            function() {
                if (hasTest && testFn.call(scope || me, me) === false) {
                    return;
                }
                Ext.fly(dom, INTERNAL).addCls(className);
            },
            function() {
                Ext.fly(dom, INTERNAL).removeCls(className);
            }
        );
        return me;
    },

    
    addClsOnFocus : function(className, testFn, scope) {
        var me = this,
            dom = me.dom,
            hasTest = Ext.isFunction(testFn);
            
        me.on("focus", function() {
            if (hasTest && testFn.call(scope || me, me) === false) {
                return false;
            }
            Ext.fly(dom, INTERNAL).addCls(className);
        });
        me.on("blur", function() {
            Ext.fly(dom, INTERNAL).removeCls(className);
        });
        return me;
    },

    
    addClsOnClick : function(className, testFn, scope) {
        var me = this,
            dom = me.dom,
            hasTest = Ext.isFunction(testFn);
            
        me.on("mousedown", function() {
            if (hasTest && testFn.call(scope || me, me) === false) {
                return false;
            }
            Ext.fly(dom, INTERNAL).addCls(className);
            var d = Ext.getDoc(),
                fn = function() {
                    Ext.fly(dom, INTERNAL).removeCls(className);
                    d.removeListener("mouseup", fn);
                };
            d.on("mouseup", fn);
        });
        return me;
    },

    
    getStyleSize : function() {
        var me = this,
            d = this.dom,
            isDoc = DOCORBODYRE.test(d.nodeName),
            s ,
            w, h;

        
        if (isDoc) {
            return {
                width : Element.getViewWidth(),
                height : Element.getViewHeight()
            };
        }

        s = me.getStyle([HEIGHT, WIDTH], true);  
        
        if (s.width && s.width != 'auto') {
            w = parseFloat(s.width);
            if (me.isBorderBox()) {
                w -= me.getFrameWidth('lr');
            }
        }
        
        if (s.height && s.height != 'auto') {
            h = parseFloat(s.height);
            if (me.isBorderBox()) {
                h -= me.getFrameWidth('tb');
            }
        }
        
        return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
    },

    statics: {
        selectableCls: Ext.baseCSSPrefix + 'selectable',
        unselectableCls: Ext.baseCSSPrefix + 'unselectable'
    },

    
    selectable : function() {
        var me = this;

        
        
        me.dom.unselectable = '';

        me.removeCls(Element.unselectableCls);
        me.addCls(Element.selectableCls);

        return me;
    },

    
    unselectable : function() {
        
        
        
        
        
        
        
        var me = this;

        
        
        
        
        
        if (Ext.isOpera) {
            me.dom.unselectable = 'on';
        }

        
        
        
        
        
        
        
        
        
        me.removeCls(Element.selectableCls);
        me.addCls(Element.unselectableCls);

        return me;
    },

    
    setVertical: function(angle, cls) {
        var me = this,
            proto = Element.prototype,
            hooks;

        me.vertical = true;
        if (cls) {
            me.addCls(me.verticalCls = cls);
        }

        me.setWidth = proto.setHeight;
        me.setHeight = proto.setWidth;
        if (!Ext.isIE9m) {
            
            
            
            
            me.getWidth = proto.getHeight;
            me.getHeight = proto.getWidth;
        }

        
        me.styleHooks = (angle === 270) ?
            Element.prototype.verticalStyleHooks270 : Element.prototype.verticalStyleHooks90;
    },

    
    setHorizontal: function() {
        var me = this,
            cls = me.verticalCls;

        delete me.vertical;
        if (cls) {
            delete me.verticalCls;
            me.removeCls(cls);
        }

        
        delete me.setWidth;
        delete me.setHeight;
        if (!Ext.isIE9m) {
            delete me.getWidth;
            delete me.getHeight;
        }

        
        delete me.styleHooks;
    }
});

Element.prototype.styleHooks = styleHooks = Ext.dom.AbstractElement.prototype.styleHooks;



Element.prototype.verticalStyleHooks90 = verticalStyleHooks90 = Ext.Object.chain(Element.prototype.styleHooks);
Element.prototype.verticalStyleHooks270 = verticalStyleHooks270 = Ext.Object.chain(Element.prototype.styleHooks);

verticalStyleHooks90.width = { name: 'height' };
verticalStyleHooks90.height = { name: 'width' };
verticalStyleHooks90['margin-top'] = { name: 'marginLeft' };
verticalStyleHooks90['margin-right'] = { name: 'marginTop' };
verticalStyleHooks90['margin-bottom'] = { name: 'marginRight' };
verticalStyleHooks90['margin-left'] = { name: 'marginBottom' };
verticalStyleHooks90['padding-top'] = { name: 'paddingLeft' };
verticalStyleHooks90['padding-right'] = { name: 'paddingTop' };
verticalStyleHooks90['padding-bottom'] = { name: 'paddingRight' };
verticalStyleHooks90['padding-left'] = { name: 'paddingBottom' };
verticalStyleHooks90['border-top'] = { name: 'borderLeft' };
verticalStyleHooks90['border-right'] = { name: 'borderTop' };
verticalStyleHooks90['border-bottom'] = { name: 'borderRight' };
verticalStyleHooks90['border-left'] = { name: 'borderBottom' };

verticalStyleHooks270.width = { name: 'height' };
verticalStyleHooks270.height = { name: 'width' };
verticalStyleHooks270['margin-top'] = { name: 'marginRight' };
verticalStyleHooks270['margin-right'] = { name: 'marginBottom' };
verticalStyleHooks270['margin-bottom'] = { name: 'marginLeft' };
verticalStyleHooks270['margin-left'] = { name: 'marginTop' };
verticalStyleHooks270['padding-top'] = { name: 'paddingRight' };
verticalStyleHooks270['padding-right'] = { name: 'paddingBottom' };
verticalStyleHooks270['padding-bottom'] = { name: 'paddingLeft' };
verticalStyleHooks270['padding-left'] = { name: 'paddingTop' };
verticalStyleHooks270['border-top'] = { name: 'borderRight' };
verticalStyleHooks270['border-right'] = { name: 'borderBottom' };
verticalStyleHooks270['border-bottom'] = { name: 'borderLeft' };
verticalStyleHooks270['border-left'] = { name: 'borderTop' };

if (Ext.isIE7m) {
    styleHooks.fontSize = styleHooks['font-size'] = {
        name: 'fontSize',
        canThrow: true
    };
    
    styleHooks.fontStyle = styleHooks['font-style'] = {
        name: 'fontStyle',
        canThrow: true
    };
    
    styleHooks.fontFamily = styleHooks['font-family'] = {
        name: 'fontFamily',
        canThrow: true
    };
}


if (Ext.isIEQuirks || Ext.isIE && Ext.ieVersion <= 8) {
    function getBorderWidth (dom, el, inline, style) {
        if (style[this.styleName] == 'none') {
            return '0px';
        }
        return style[this.name];
    }

    edges = ['Top','Right','Bottom','Left'];
    k = edges.length;

    while (k--) {
        edge = edges[k];
        borderWidth = 'border' + edge + 'Width';

        styleHooks['border-'+edge.toLowerCase()+'-width'] = styleHooks[borderWidth] = {
            name: borderWidth,
            styleName: 'border' + edge + 'Style',
            get: getBorderWidth
        };
    }
}

































Ext.getDoc().on('selectstart', function(ev, dom) {
    var doc = document.documentElement,
        selectableCls = Element.selectableCls,
        unselectableCls = Element.unselectableCls,
        tagName = dom && dom.tagName;

    tagName = tagName && tagName.toLowerCase();

    
    
    
    if (tagName === 'input' || tagName === 'textarea') {
        return;
    }

    
    while (dom && dom.nodeType === 1 && dom !== doc) {
        var el = Ext.fly(dom);

        
        if (el.hasCls(selectableCls)) {
            return;
        }

        
        if (el.hasCls(unselectableCls)) {
            ev.stopEvent();
            return;
        }

        dom = dom.parentNode;
    }
});

});

Ext.onReady(function () {
    var opacityRe = /alpha\(opacity=(.*)\)/i,
        trimRe = /^\s+|\s+$/g,
        hooks = Ext.dom.Element.prototype.styleHooks;

    
    hooks.opacity = {
        name: 'opacity',
        afterSet: function(dom, value, el) {
            if (el.isLayer) {
                el.onOpacitySet(value);
            }
        }
    };
    if (!Ext.supports.Opacity && Ext.isIE) {
        Ext.apply(hooks.opacity, {
            get: function (dom) {
                var filter = dom.style.filter,
                    match, opacity;
                if (filter.match) {
                    match = filter.match(opacityRe);
                    if (match) {
                        opacity = parseFloat(match[1]);
                        if (!isNaN(opacity)) {
                            return opacity ? opacity / 100 : 0;
                        }
                    }
                }
                return 1;
            },
            set: function (dom, value) {
                var style = dom.style,
                    val = style.filter.replace(opacityRe, '').replace(trimRe, '');

                style.zoom = 1; 

                
                if (typeof(value) == 'number' && value >= 0 && value < 1) {
                    value *= 100;
                    style.filter = val + (val.length ? ' ' : '') + 'alpha(opacity='+value+')';
                } else {
                    style.filter = val;
                }
            }  
        });
    }
    
    
});



Ext.define('Ext.util.Positionable', {

    _positionTopLeft: ['position', 'top', 'left'],

    _alignRe: /^([a-z]+)-([a-z]+)(\?)?$/,

    
    
    afterSetPosition: Ext.emptyFn,


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

    
    anchorTo: function(anchorToEl, alignment, offsets, animate, monitorScroll, callback) {
        var me = this,
            scroll = !Ext.isEmpty(monitorScroll),
            action = function() {
                me.alignTo(anchorToEl, alignment, offsets, animate);
                Ext.callback(callback, me);
            },
            anchor = me.getAnchor();

        
        me.removeAnchor();
        Ext.apply(anchor, {
            fn: action,
            scroll: scroll
        });

        Ext.EventManager.onWindowResize(action, null);

        if (scroll) {
            Ext.EventManager.on(window, 'scroll', action, null,
                    {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
        }
        action(); 
        return me;
    },

    
    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.getViewWidth() : me.getWidth();
        myHeight = mySize.height || isViewport ? Ext.Element.getViewHeight() : 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,
            viewportWidth = Ext.Element.getViewWidth() - 10, 
            viewportHeight = Ext.Element.getViewHeight() - 10, 
            doc = document,
            docElement = doc.documentElement,
            docBody = doc.body,
            scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0),
            scrollY = (docElement.scrollTop  || docBody.scrollTop  || 0),
            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) {
        }

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


        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) {
            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 = ((p1y == "t" && p2y == "b") || (p1y == "b" && p2y == "t"));
            swapX = ((p1x == "r" && p2x == "l") || (p1x == "l" && p2x == "r"));

            if (x + myWidth > viewportWidth + scrollX) {
                x = swapX ? alignToElRegion.left - myWidth : viewportWidth + scrollX - myWidth;
            }
            if (x < scrollX) {
                x = swapX ? alignToElRegion.right : scrollX;
            }
            if (y + myHeight > viewportHeight + scrollY) {
                y = swapY ? alignToElRegion.top - myHeight : viewportHeight + scrollY - myHeight;
            }
            if (y < scrollY) {
                y = swapY ? alignToElRegion.bottom : scrollY;
            }
        }
        return [x,y];
    },

    
    getAnchor: function(){
        var el = this.el,
            data = (el.$cache || el.getCache()).data,
            anchor;
            
        if (!el.dom) {
            return;
        }
        anchor = data._anchor;

        if(!anchor){
            anchor = data._anchor = {};
        }
        return anchor;
    },

    
    getAnchorXY: function(anchor, local, mySize) {
        var me = this,
            myPos = me.getXY(),
            el = me.el,
            doc = document,
            isViewport = el.dom == doc.body || el.dom == doc,
            scroll = el.getScroll(),
            extraX = isViewport ? scroll.left : local ? 0 : myPos[0],
            extraY = isViewport ? scroll.top : local ? 0 : myPos[1];

        return me.calculateAnchorXY(anchor, extraX, extraY, mySize);
    },

    
    getBox: function(contentBox, local) {
        var me = this,
            xy = local ? me.getLocalXY() : me.getXY(),
            x = xy[0],
            y = xy[1],
            w = me.getWidth(),
            h = me.getHeight(),
            borderPadding, beforeX, beforeY;

        if (contentBox) {
            borderPadding = me.getBorderPadding();
            beforeX = borderPadding.beforeX;
            beforeY = borderPadding.beforeY;

            x += beforeX;
            y += beforeY;
            w -= (beforeX + borderPadding.afterX);
            h -= (beforeY + borderPadding.afterY);
        }

        return {
            x: x,
            left: x,
            0: x,
            y: y,
            top: y,
            1: y,
            width: w,
            height: h,
            right: x + w,
            bottom: y + h
        };
    },

    
    calculateConstrainedPosition: function(constrainTo, proposedPosition, local, proposedSize) {
        var me = this,
            vector,
            fp = me.floatParent,
            parentNode = fp ? fp.getTargetEl() : null,
            parentOffset,
            borderPadding,
            proposedConstrainPosition,
            xy = false;

        if (local && fp) {
            parentOffset = parentNode.getXY();
            borderPadding = parentNode.getBorderPadding();
            parentOffset[0] += borderPadding.beforeX;
            parentOffset[1] += borderPadding.beforeY;
            if (proposedPosition) {
                proposedConstrainPosition = [proposedPosition[0] + parentOffset[0], proposedPosition[1] + parentOffset[1]];
            }
        } else {
            proposedConstrainPosition = proposedPosition;
        }
        
        
        
        constrainTo = constrainTo || me.constrainTo || parentNode || me.container || me.el.parent();
        vector = (me.constrainHeader ? me.header : me).getConstrainVector(constrainTo, proposedConstrainPosition, proposedSize);

        
        if (vector) {
            xy = proposedPosition || me.getPosition(local);
            xy[0] += vector[0];
            xy[1] += vector[1];
        }
        return xy;
    },

    
    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,
            constraintInsets = this.constraintInsets;

        if (!(constrainTo instanceof Ext.util.Region)) {
            constrainTo = Ext.get(constrainTo.el || constrainTo).getViewRegion();
        }

        
        if (constraintInsets) {
            constraintInsets = Ext.isObject(constraintInsets) ? constraintInsets : Ext.Element.parseBox(constraintInsets);
            constrainTo.adjust(constraintInsets.top, constraintInsets.right, constraintInsets.bottom, constraintInsets.length);
        }

        
        if (proposedPosition) {
            thisRegion.translateBy(proposedPosition[0] - thisRegion.x, proposedPosition[1] - thisRegion.y);
        }
        
        if (proposedSize) {
            thisRegion.right = thisRegion.left + proposedSize[0];
            thisRegion.bottom = thisRegion.top + proposedSize[1];
        }

        
        if (shadowSize) {
            constrainTo.adjust(shadowSize[0], -shadowSize[1], -shadowSize[2], shadowSize[3]);
        }

        
        if (thisRegion.right > constrainTo.right) {
            overflowed = true;
            vector[0] = (constrainTo.right - thisRegion.right);    
        }
        if (thisRegion.left + vector[0] < constrainTo.left) {
            overflowed = true;
            vector[0] = (constrainTo.left - thisRegion.left);      
        }

        
        if (thisRegion.bottom > constrainTo.bottom) {
            overflowed = true;
            vector[1] = (constrainTo.bottom - thisRegion.bottom);  
        }
        if (thisRegion.top + vector[1] < constrainTo.top) {
            overflowed = true;
            vector[1] = (constrainTo.top - thisRegion.top);        
        }
        return overflowed ? vector : false;
    },

    
    getOffsetsTo: function(offsetsTo) {
        var o = this.getXY(),
                e = Ext.fly(offsetsTo.el || offsetsTo, '_internal').getXY();
        return [o[0] - e[0],o[1] - e[1]];
    },

    
    getRegion: function() {
        var box = this.getBox();
        return new Ext.util.Region(box.top, box.right, box.bottom, box.left);
    },

    
    getViewRegion: function() {
        var me = this,
            el = me.el,
            isBody = el.dom.nodeName === 'BODY',
            borderPadding, scroll, pos, top, left, width, height;

        
        if (isBody) {
            scroll = el.getScroll();
            left = scroll.left;
            top = scroll.top;
            width = Ext.dom.AbstractElement.getViewportWidth();
            height = Ext.dom.AbstractElement.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);
    },

    
    removeAnchor: function() {
        var anchor = this.getAnchor();

        if (anchor && anchor.fn) {
            Ext.EventManager.removeResizeListener(anchor.fn);
            if (anchor.scroll) {
                Ext.EventManager.un(window, 'scroll', anchor.fn);
            }
            delete anchor.fn;
        }
        return this;
    },

    
    setBox: function(box, animate) {
        var me = this,
            el = me.el,
            x = box.x,
            y = box.y,
            xy = [x, y],
            w = box.width,
            h = box.height,
            doConstrain = (me.constrain || me.constrainHeader),
            constrainedPos = doConstrain && me.calculateConstrainedPosition(null, [x, y], false, [w, h]);

        
        if (constrainedPos) {
            x = constrainedPos[0];
            y = constrainedPos[1];
        }
        if (!animate || !el.anim) {
            me.setSize(w, h);
            me.setXY([x, y]);
            me.afterSetPosition(x, y);
        } else {
            me.animate(Ext.applyIf({
                to: {
                    x: x,
                    y: y,
                    width: el.adjustWidth(w),
                    height: el.adjustHeight(h)
                },
                listeners: {
                    afteranimate: Ext.Function.bind(me.afterSetPosition, me, [x, y])
                }
            }, animate));
        }
        return me;
    },

    
    setRegion: function(region, animate) {
        return this.setBox({
            x: region.left,
            y: region.top,
            width: region.right - region.left,
            height: region.bottom - region.top
        }, animate);
    },

    
    translatePoints: function(x, y) {
        var pos = this.translateXY(x, y);

        return {
            left: pos.x,
            top: pos.y
        };
    },

    
    translateXY: function(x, y) {
        var me = this,
            el = me.el,
            styles = el.getStyle(me._positionTopLeft),
            relative = styles.position == 'relative',
            left = parseFloat(styles.left),
            top = parseFloat(styles.top),
            xy = me.getXY();

        if (Ext.isArray(x)) {
             y = x[1];
             x = x[0];
        }
        if (isNaN(left)) {
            left = relative ? 0 : el.dom.offsetLeft;
        }
        if (isNaN(top)) {
            top = relative ? 0 : el.dom.offsetTop;
        }
        left = (typeof x == 'number') ? x - xy[0] + left : undefined;
        top = (typeof y == 'number') ? y - xy[1] + top : undefined;
        return {
            x: left,
            y: top
        };
    }
});



Ext.define('Ext.dom.Element', function(Element) {
    var HIDDEN          = 'hidden',
        DOC             = document,
        VISIBILITY      = "visibility",
        DISPLAY         = "display",
        NONE            = "none",
        XMASKED         = Ext.baseCSSPrefix + "masked",
        XMASKEDRELATIVE = Ext.baseCSSPrefix + "masked-relative",
        EXTELMASKMSG    = Ext.baseCSSPrefix + "mask-msg",
        bodyRe          = /^body/i,
        visFly,

        
        noBoxAdjust = Ext.isStrict ? {
            select: 1
        }: {
            input: 1,
            select: 1,
            textarea: 1
        },

        
        isScrolled = function(c) {
            var r = [], ri = -1,
                i, ci;
            for (i = 0; ci = c[i]; i++) {
                if (ci.scrollTop > 0 || ci.scrollLeft > 0) {
                    r[++ri] = ci;
                }
            }
            return r;
        };

    return {

        extend:  Ext.dom.AbstractElement ,

        alternateClassName: ['Ext.Element', 'Ext.core.Element'],

                   
                            
                                   
                                 
                                 
                                       
                                     
                                   
          
        
        tableTagRe: /^(?:tr|td|table|tbody)$/i,

        mixins: [
             Ext.util.Positionable 
        ],

        addUnits: function() {
            return Element.addUnits.apply(Element, arguments);
        },

        
        focus: function(defer,  dom) {
            var me = this;

            dom = dom || me.dom;
            try {
                if (Number(defer)) {
                    Ext.defer(me.focus, defer, me, [null, dom]);
                } else {
                    dom.focus();
                }
            } catch(e) {
            }
            return me;
        },

        
        blur: function() {
            var me = this,
                dom = me.dom;
            
            
            if (dom !== document.body) {
                try {
                    dom.blur();
                } catch(e) {
                }
                return me;
            } else {
                return me.focus(undefined, dom);
            }
        },

        
        isBorderBox: function() {
            var box = Ext.isBorderBox;
            
            
            if (box && Ext.isIE7m) {
                box = !((this.dom.tagName || "").toLowerCase() in noBoxAdjust);
            }
            return box;
        },

        
        hover: function(overFn, outFn, scope, options) {
            var me = this;
            me.on('mouseenter', overFn, scope || me.dom, options);
            me.on('mouseleave', outFn, scope || me.dom, options);
            return me;
        },

        
        getAttributeNS: function(ns, name) {
            return this.getAttribute(name, ns);
        },

        getAttribute: (Ext.isIE && !(Ext.isIE9p && DOC.documentMode >= 9)) ?

            
            
            
            
            
            
            
            
            

            function(name, ns) {
                var d = this.dom,
                        type;
                if (ns) {
                    type = typeof d[ns + ":" + name];
                    if (type != 'undefined' && type != 'unknown') {
                        return d[ns + ":" + name] || null;
                    }
                    return null;
                }
                if (name === "for") {
                    name = "htmlFor";
                }
                return d[name] || null;
            } : function(name, ns) {
                var d = this.dom;
                if (ns) {
                    return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name);
                }
                return  d.getAttribute(name) || d[name] || null;
            },

        
        cacheScrollValues: function() {
            var me = this,
                scrolledDescendants,
                el, i,
                scrollValues = [],
                result = function() {
                    for (i = 0; i < scrolledDescendants.length; i++) {
                        el = scrolledDescendants[i];
                        el.scrollLeft = scrollValues[i][0];
                        el.scrollTop  = scrollValues[i][1];
                    }
                };

            if (!Ext.DomQuery.pseudos.isScrolled) {
                Ext.DomQuery.pseudos.isScrolled = isScrolled;
            }
            scrolledDescendants = me.query(':isScrolled');
            for (i = 0; i < scrolledDescendants.length; i++) {
                el = scrolledDescendants[i];
                scrollValues[i] = [el.scrollLeft, el.scrollTop];
            }
            return result;
        },

        
        autoBoxAdjust: true,

        
        isVisible : function(deep) {
            var me = this,
                dom = me.dom,
                stopNode = dom.ownerDocument.documentElement;

            if (!visFly) {
                visFly = new Element.Fly();
            }

            while (dom !== stopNode) {
                
                
                if (!dom || dom.nodeType === 11 || (visFly.attach(dom)).isStyle(VISIBILITY, HIDDEN) || visFly.isStyle(DISPLAY, NONE)) {
                    return false;
                }
                
                if (!deep) {
                    break;
                }
                dom = dom.parentNode;
            }
            return true;
        },

        
        isDisplayed : function() {
            return !this.isStyle(DISPLAY, NONE);
        },

        
        enableDisplayMode : function(display) {
            var me = this;

            me.setVisibilityMode(Element.DISPLAY);

            if (!Ext.isEmpty(display)) {
                (me.$cache || me.getCache()).data.originalDisplay = display;
            }

            return me;
        },

        
        mask : function(msg, msgCls , elHeight) {
            var me            = this,
                dom           = me.dom,
                
                
                setExpression = dom.style.setExpression,
                data          = (me.$cache || me.getCache()).data,
                maskShimEl    = data.maskShimEl,
                maskEl        = data.maskEl,
                maskMsg       = data.maskMsg,
                widthExpression, heightExpression, docElem, ie6DocElHeight;

            if (!(bodyRe.test(dom.tagName) && me.getStyle('position') == 'static')) {
                me.addCls(XMASKEDRELATIVE);
            }

            
            if (maskEl) {
                maskEl.remove();
            }

            if (maskMsg) {
                maskMsg.remove();
            }

            if (maskShimEl) {
                maskShimEl.remove();
            }

            if (Ext.isIE6) {
                maskShimEl = Ext.DomHelper.append(dom, {
                    tag: 'iframe',
                    role: 'presentation',
                    cls : Ext.baseCSSPrefix + 'shim ' + Ext.baseCSSPrefix + 'mask-shim'
                }, true);
                data.maskShimEl = maskShimEl;
                maskShimEl.setDisplayed(true);
            }

            Ext.DomHelper.append(dom, [{
                role: 'presentation',
                cls : Ext.baseCSSPrefix + "mask",
                style: 'top:0;left:0;'
            }, {
                role: 'presentation',
                cls : msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG,
                cn  : {
                    tag: 'div',
                    role: 'presentation',
                    cls: Ext.baseCSSPrefix + 'mask-msg-inner',
                    cn: {
                        tag: 'div',
                        role: 'presentation',
                        cls: Ext.baseCSSPrefix + 'mask-msg-text',
                        html: msg || ''
                    }
                }
            }]);

            maskMsg = Ext.get(dom.lastChild);
            maskEl = Ext.get(maskMsg.dom.previousSibling);

            data.maskMsg = maskMsg;
            data.maskEl = maskEl;

            me.addCls(XMASKED);
            maskEl.setDisplayed(true);

            if (typeof msg == 'string') {
                maskMsg.setDisplayed(true);
                maskMsg.center(me);
            } else {
                maskMsg.setDisplayed(false);
            }

            
            
            
            
            if (Ext.isStrict && !Ext.isIE6) {
                if (dom === DOC.body) {
                    maskEl.addCls(Ext.baseCSSPrefix + 'mask-fixed');
                }
            }

            
            
            
            
            
            
            
            
            if (dom !== DOC.body || Ext.isIE6 || Ext.isIEQuirks) {
                if (!Ext.supports.IncludePaddingInWidthCalculation && setExpression) {
                    
                    try {
                        maskEl.dom.style.setExpression('width', 'this.parentNode.clientWidth + "px"');
                        widthExpression = 'this.parentNode.clientWidth + "px"';
                        if (maskShimEl) {
                            maskShimEl.dom.style.setExpression('width', widthExpression);
                        }
                        maskEl.dom.style.setExpression('width', widthExpression);
                    } catch (e) {}
                }

                
                
                if (!Ext.supports.IncludePaddingInHeightCalculation && setExpression) {
                    
                    try {
                        heightExpression = 'this.parentNode.' + (dom == DOC.body ? 'scrollHeight' : 'offsetHeight') + ' + "px"';
                        if (maskShimEl) {
                            maskShimEl.dom.style.setExpression('height', heightExpression);
                        }
                        maskEl.dom.style.setExpression('height', heightExpression);
                    } catch (e) {}
                }
                
                else if (Ext.isIE9m && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto') {
                    if (Ext.isIE6 && Ext.isStrict) {
                        docElem = dom.parentNode;
                        
                        
                        
                        
                        
                        
                        ie6DocElHeight = Math.max(docElem.clientHeight, docElem.scrollHeight);
                    }
                    if (maskShimEl) {
                        maskShimEl.setSize(undefined, elHeight || ie6DocElHeight || me.getHeight());
                    }
                    maskEl.setSize(undefined, elHeight || ie6DocElHeight || me.getHeight());
                }
            }
            return maskEl;
        },

        
        unmask : function() {
            var me      = this,
                data    = (me.$cache || me.getCache()).data,
                maskEl  = data.maskEl,
                maskShimEl = data.maskShimEl,
                maskMsg = data.maskMsg,
                style;

            if (maskEl) {
                style = maskEl.dom.style;
                
                if (style.clearExpression) {
                    style.clearExpression('width');
                    style.clearExpression('height');
                }

                if (maskEl) {
                    maskEl.remove();
                    delete data.maskEl;
                }

                if (maskMsg) {
                    maskMsg.remove();
                    delete data.maskMsg;
                }

                me.removeCls([XMASKED, XMASKEDRELATIVE]);

                if (maskShimEl) {
                    style = maskShimEl.dom.style;
                    
                    if (style.clearExpression) {
                        style.clearExpression('width');
                        style.clearExpression('height');
                    }

                    maskShimEl.remove();
                    delete data.maskShimEl;
                }
            }
        },

        
        isMasked : function() {
            var me      = this,
                data    = (me.$cache || me.getCache()).data,
                maskEl  = data.maskEl,
                maskMsg = data.maskMsg,
                hasMask = false; 

            if (maskEl && maskEl.isVisible()) {
                if (maskMsg) {
                    maskMsg.center(me);
                }
                hasMask = true;
            }
            return hasMask;
        },

        
        createShim : function() {
            var el = DOC.createElement('iframe'),
                shim;

            el.frameBorder = '0';
            el.className = Ext.baseCSSPrefix + 'shim';
            el.src = Ext.SSL_SECURE_URL;
            el.setAttribute('role', 'presentation');
            shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
            shim.autoBoxAdjust = false;
            return shim;
        },

        
        addKeyListener : function(key, fn, scope){
            var config;
            if(typeof key != 'object' || Ext.isArray(key)){
                config = {
                    target: this,
                    key: key,
                    fn: fn,
                    scope: scope
                };
            }else{
                config = {
                    target: this,
                    key : key.key,
                    shift : key.shift,
                    ctrl : key.ctrl,
                    alt : key.alt,
                    fn: fn,
                    scope: scope
                };
            }
            return new Ext.util.KeyMap(config);
        },

        
        addKeyMap : function(config) {
            return new Ext.util.KeyMap(Ext.apply({
                target: this
            }, config));
        },

        
        
        
        
        
        
        
        
        
        
        

        
        
        
        

        
        
        
        
        
        
        

        
        
        
        
        
        
        

        
        
        
        

        
        
        
        
        
        
        
        

        
        on: function(eventName, fn, scope, options) {
            Ext.EventManager.on(this, eventName, fn, scope || this, options);
            return this;
        },

        
        un: function(eventName, fn, scope) {
            Ext.EventManager.un(this, eventName, fn, scope || this);
            return this;
        },

        
        removeAllListeners: function() {
            Ext.EventManager.removeAll(this);
            return this;
        },

        
        purgeAllListeners: function() {
            Ext.EventManager.purgeElement(this);
            return this;
        },

        select: function(selector) {
            return Element.select(selector, false,  this.dom);
        }
    };
}, function() {

    var DOC             = document,
        EC              = Ext.cache,
        Element         = this,
        AbstractElement = Ext.dom.AbstractElement,
        focusRe         = /^a|button|embed|iframe|input|object|select|textarea$/i,
        nonSpaceRe      = /\S/,
        scriptTagRe     = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
        replaceScriptTagRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
        srcRe           = /\ssrc=([\'\"])(.*?)\1/i,
        typeRe          = /\stype=([\'\"])(.*?)\1/i,
        useDocForId     = !Ext.isIE8m,
        internalFly;

    Element.boxMarkup = [
        '<div class="{0}-tl" role="presentation">',
            '<div class="{0}-tr" role="presentation">',
                '<div class="{0}-tc" role="presentation"></div>',
            '</div>',
        '</div>',
        '<div class="{0}-ml" role="presentation">',
            '<div class="{0}-mr" role="presentation">',
                '<div class="{0}-mc" role="presentation"></div>',
            '</div>',
        '</div>',
        '<div class="{0}-bl" role="presentation">',
            '<div class="{0}-br" role="presentation">',
                '<div class="{0}-bc" role="presentation"></div>',
            '</div>',
        '</div>'
    ].join('');
    

    
    
    
    function garbageCollect() {
        if (!Ext.enableGarbageCollector) {
            clearInterval(Element.collectorThreadId);
        } else {
            var eid,
                d,
                o,
                t;

            for (eid in EC) {
                if (!EC.hasOwnProperty(eid)) {
                    continue;
                }

                o = EC[eid];

                
                if (o.skipGarbageCollection) {
                    continue;
                }

                d = o.dom;


                
                
                
                
                
                
                
                
                
                
                
                
                
                
                if (d && (!d.parentNode || (!d.offsetParent && !Ext.getElementById(eid)))) {
                    if (Ext.enableListenerCollection) {
                        Ext.EventManager.removeAll(d);
                    }
                    delete EC[eid];
                }
            }
            
            if (Ext.isIE) {
                t = {};
                for (eid in EC) {
                    if (!EC.hasOwnProperty(eid)) {
                        continue;
                    }
                    t[eid] = EC[eid];
                }
                EC = Ext.cache = t;
            }
        }
    }

    Element.collectorThreadId = setInterval(garbageCollect, 30000);

    
    Element.addMethods({

        
        monitorMouseLeave: function(delay, handler, scope) {
            var me = this,
                timer,
                listeners = {
                    mouseleave: function(e) {
                        timer = setTimeout(Ext.Function.bind(handler, scope||me, [e]), delay);
                    },
                    mouseenter: function() {
                        clearTimeout(timer);
                    },
                    freezeEvent: true
                };

            me.on(listeners);
            return listeners;
        },

        
        swallowEvent : function(eventName, preventDefault) {
            var me = this,
                e, eLen,
                fn = function(e) {
                    e.stopPropagation();
                    if (preventDefault) {
                        e.preventDefault();
                    }
                };

            if (Ext.isArray(eventName)) {
                eLen = eventName.length;

                for (e = 0; e < eLen; e++) {
                    me.on(eventName[e], fn);
                }

                return me;
            }
            me.on(eventName, fn);
            return me;
        },

        
        relayEvent : function(eventName, observable) {
            this.on(eventName, function(e) {
                observable.fireEvent(eventName, e);
            });
        },

        
        clean : function(forceReclean) {
            var me   = this,
                dom  = me.dom,
                data = (me.$cache || me.getCache()).data,
                n    = dom.firstChild,
                ni   = -1,
                nx;

            if (data.isCleaned && forceReclean !== true) {
                return me;
            }

            while (n) {
                nx = n.nextSibling;
                if (n.nodeType == 3) {
                    
                    if (!(nonSpaceRe.test(n.nodeValue))) {
                        dom.removeChild(n);
                    
                    } else if (nx && nx.nodeType == 3) {
                        n.appendData(Ext.String.trim(nx.data));
                        dom.removeChild(nx);
                        nx = n.nextSibling;
                        n.nodeIndex = ++ni;
                    }
                } else {
                    
                    internalFly.attach(n).clean();
                    n.nodeIndex = ++ni;
                }
                n = nx;
            }

            data.isCleaned = true;
            return me;
        },

        
        load : function(options) {
            this.getLoader().load(options);
            return this;
        },

        
        getLoader : function() {
            var me = this,
                data = (me.$cache || me.getCache()).data,
                loader = data.loader;

            if (!loader) {
                data.loader = loader = new Ext.ElementLoader({
                    target: me
                });
            }
            return loader;
        },

        
        syncContent: function(source) {
            source = Ext.getDom(source);
            var sourceNodes = source.childNodes,
                sourceLen = sourceNodes.length,
                dest = this.dom,
                destNodes = dest.childNodes,
                destLen = destNodes.length,
                i,  destNode, sourceNode,
                nodeType, newAttrs, attLen, attName;

            
            
            
            
            if (Ext.isIE9m && dest.mergeAttributes) {
                dest.mergeAttributes(source, true);

                
                
                dest.src = source.src;
            } else {
                newAttrs = source.attributes;
                attLen = newAttrs.length;
                for (i = 0; i < attLen; i++) {
                    attName = newAttrs[i].name;
                    if (attName !== 'id') {
                        dest.setAttribute(attName, newAttrs[i].value);
                    }
                }
            }

            
            if (sourceLen !== destLen) {
                dest.innerHTML = source.innerHTML;
                return;
            }

            
            
            for (i = 0; i < sourceLen; i++) {
                sourceNode = sourceNodes[i];
                destNode = destNodes[i];
                nodeType = sourceNode.nodeType;

                
                if (nodeType !== destNode.nodeType || (nodeType === 1 && sourceNode.tagName !== destNode.tagName)) {
                    dest.innerHTML = source.innerHTML;
                    return;
                }

                
                if (nodeType === 3) {
                    destNode.data = sourceNode.data;
                }
                
                else {
                    if (sourceNode.id && destNode.id !== sourceNode.id) {
                        destNode.id = sourceNode.id;
                    }
                    destNode.style.cssText = sourceNode.style.cssText;
                    destNode.className = sourceNode.className;
                    internalFly.attach(destNode).syncContent(sourceNode);
                }
            }
        },

        
        update : function(html, loadScripts, callback) {
            var me = this,
                id,
                dom,
                interval;

            if (!me.dom) {
                return me;
            }
            html = html || '';
            dom = me.dom;

            if (loadScripts !== true) {
                dom.innerHTML = html;
                Ext.callback(callback, me);
                return me;
            }

            id  = Ext.id();
            html += '<span id="' + id + '" role="presentation"></span>';

            interval = setInterval(function() {
                var hd,
                    match,
                    attrs,
                    srcMatch,
                    typeMatch,
                    el,
                    s;
                if (!(el = DOC.getElementById(id))) {
                    return false;
                }
                clearInterval(interval);
                Ext.removeNode(el);
                hd = Ext.getHead().dom;

                while ((match = scriptTagRe.exec(html))) {
                    attrs = match[1];
                    srcMatch = attrs ? attrs.match(srcRe) : false;
                    if (srcMatch && srcMatch[2]) {
                       s = DOC.createElement("script");
                       s.src = srcMatch[2];
                       typeMatch = attrs.match(typeRe);
                       if (typeMatch && typeMatch[2]) {
                           s.type = typeMatch[2];
                       }
                       hd.appendChild(s);
                    } else if (match[2] && match[2].length > 0) {
                        if (window.execScript) {
                           window.execScript(match[2]);
                        } else {
                           window.eval(match[2]);
                        }
                    }
                }
                Ext.callback(callback, me);
            }, 20);
            dom.innerHTML = html.replace(replaceScriptTagRe, '');
            return me;
        },

        
        removeAllListeners : function() {
            this.removeAnchor();
            Ext.EventManager.removeAll(this.dom);
            return this;
        },

        
        createProxy : function(config, renderTo, matchBox) {
            config = (typeof config == 'object') ? config : { tag: "div", role: 'presentation', cls: config };

            var me = this,
                proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) :
                                   Ext.DomHelper.insertBefore(me.dom, config, true);

            proxy.setVisibilityMode(Element.DISPLAY);
            proxy.hide();
            if (matchBox && me.setBox && me.getBox) { 
               proxy.setBox(me.getBox());
            }
            return proxy;
        },
        
        
        needsTabIndex: function() {
            if (this.dom) {
                if ((this.dom.nodeName === 'a') && (!this.dom.href)) {
                    return true;
                }
                return !focusRe.test(this.dom.nodeName);
            }
        },

        
        isFocusable: function ( asFocusEl) {
            var dom = this.dom,
                tabIndexAttr = dom.getAttributeNode('tabIndex'),
                tabIndex,
                nodeName = dom.nodeName,
                canFocus = false;

            
            
            
            
            
            
            
            
            
            
            if (tabIndexAttr && tabIndexAttr.specified) {
                tabIndex = tabIndexAttr.value;
            }
            if (dom && !dom.disabled) {
                
                
                if (tabIndex == -1) { 
                    canFocus = Ext.FocusManager && Ext.FocusManager.enabled && asFocusEl;
                }
                else {
                    
                    if (focusRe.test(nodeName)) {
                        if ((nodeName !== 'a') || dom.href) {
                            canFocus = true;
                        }
                    }
                    
                    else {
                        canFocus = tabIndex != null && tabIndex >= 0;
                    }
                }
                canFocus = canFocus && this.isVisible(true);
            }
            return canFocus;
        }
    });

    if (Ext.isIE9m) {
        Element.prototype.getById = function (id, asDom) {
            var dom = this.dom,
                cacheItem, el, ret;

            if (dom) {
                
                
                el = (useDocForId && DOC.getElementById(id)) || dom.all[id];
                if (el) {
                    if (asDom) {
                        ret = el;
                    } else {
                        
                        
                        cacheItem = EC[id];
                        if (cacheItem && cacheItem.el) {
                            ret = Ext.updateCacheEntry(cacheItem, el).el;
                        } else {
                            ret = new Element(el);
                        }
                    }
                    return ret;
                }
            }

            return asDom ? Ext.getDom(id) : Element.get(id);
        };
    }

    Element.createAlias({
        
        addListener: 'on',
        
        removeListener: 'un',
        
        clearListeners: 'removeAllListeners',
        
        focusable: 'isFocusable'
    });

    Element.Fly = AbstractElement.Fly = new Ext.Class({
        extend: Element,

        isFly: true,

        constructor: function(dom) {
            this.dom = dom;
            
            
            
            this.el = this;
        },
        
        attach: AbstractElement.Fly.prototype.attach
    });
    
    internalFly = new Element.Fly();

    if (Ext.isIE9m) {
        Ext.getElementById = function (id) {
            var el = DOC.getElementById(id),
                detachedBodyEl;

            if (!el && (detachedBodyEl = AbstractElement.detachedBodyEl)) {
                el = detachedBodyEl.dom.all[id];
            }

            return el;
        };
    } else if (!DOC.querySelector) {
        Ext.getDetachedBody = Ext.getBody;

        Ext.getElementById = function (id) {
            return DOC.getElementById(id);
        };
    }
});



Ext.define('Ext.dom.CompositeElementLite', {
    alternateClassName: 'Ext.CompositeElementLite',

                                                   

    statics: {
        
        importElementMethods: function() {
            var name,
                elementPrototype = Ext.dom.Element.prototype,
                prototype = this.prototype;

            for (name in elementPrototype) {
                if (typeof elementPrototype[name] == 'function'){
                    (function(key) {
                        prototype[key] = prototype[key] || function() {
                            return this.invoke(key, arguments);
                        };
                    }).call(prototype, name);

                }
            }
        }
    },

    constructor: function(elements, root) {
        
        this.elements = [];
        this.add(elements, root);
        this.el = new Ext.dom.AbstractElement.Fly();
    },

    
    isComposite: true,

    
    getElement: function(el) {
        
        return this.el.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.dom.Element.selectorFunction(els, root);
        }
        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 elements = this.elements,
            ln = elements.length,
            element,
            i;

        fn = Ext.dom.Element.prototype[fn];
        for (i = 0; i < ln; i++) {
            element = elements[i];

            if (element) {
                fn.apply(this.getElement(element), args);
            }
        }
        return this;
    },

    
    item: function(index) {
        var el = this.elements[index],
            out = null;

        if (el) {
            out = this.getElement(el);
        }

        return out;
    },

    
    slice: function() {
        return this.elements.slice.apply(this.elements, arguments);
    },

    
    addListener: function(eventName, handler, scope, opt) {
        var els = this.elements,
                len = els.length,
                i, e;

        for (i = 0; i < len; i++) {
            e = els[i];
            if (e) {
                Ext.EventManager.on(e, eventName, handler, scope || e, opt);
            }
        }
        return this;
    },
    
    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.remove();
                    } else {
                        Ext.removeNode(el);
                    }
                }
                Ext.Array.erase(elements, val, 1);
            }
        }

        return me;
    }

}, function() {
    this.importElementMethods();

    this.prototype.on = this.prototype.addListener;

    if (Ext.DomQuery){
        Ext.dom.Element.selectorFunction = Ext.DomQuery.select;
    }

    
   Ext.dom.Element.select = function(selector, root) {
        var elements;

        if (typeof selector == "string") {
            elements = Ext.dom.Element.selectorFunction(selector, root);
        }
        else if (selector.length !== undefined) {
            elements = selector;
        }
        else {
        }

        return new Ext.CompositeElementLite(elements);
    };

    
    Ext.select = function() {
        return Ext.dom.Element.select.apply(Ext.dom.Element, arguments);
    };
});



Ext.define('Ext.dom.CompositeElement', {
    alternateClassName: 'Ext.CompositeElement',

    extend:  Ext.dom.CompositeElementLite ,

    
    getElement: function(el) {
        
        return el;
    },

    
    transformElement: function(el) {
        return Ext.get(el);
    }

}, function() {
    

    Ext.dom.Element.select = function(selector, unique, root) {
        var elements;

        if (typeof selector == "string") {
            elements = Ext.dom.Element.selectorFunction(selector, root);
        }
        else if (selector.length !== undefined) {
            elements = selector;
        }
        else {
        }
        return (unique === true) ? new Ext.CompositeElement(elements) : new Ext.CompositeElementLite(elements);
    };
});


Ext.select = Ext.Element.select;


Ext.define('Ext.util.HashMap', {
    mixins: {
        observable:  Ext.util.Observable 
    },

    
    generation: 0,
    
    

    
    constructor: function(config) {
        config = config || {};

        var me = this,
            keyFn = config.keyFn;

        me.initialConfig = config;
        me.addEvents(
            
            'add',
            
            'clear',
            
            'remove',
            
            'replace'
        );

        me.mixins.observable.constructor.call(me, config);
        me.clear(true);

        if (keyFn) {
            me.getKey = keyFn;
        }
    },

    
    getCount: function() {
        return this.length;
    },

    
    getData: function(key, value) {
        
        if (value === undefined) {
            value = key;
            key = this.getKey(value);
        }

        return [key, value];
    },

    
    getKey: function(o) {
        return o.id;
    },

    
    add: function(key, value) {
        var me = this;

        
        
        if (arguments.length === 1) {
            value = key;
            key = me.getKey(value);
        }

        if (me.containsKey(key)) {
            return me.replace(key, value);
        }

        me.map[key] = value;
        ++me.length;
        me.generation++;
        if (me.hasListeners.add) {
            me.fireEvent('add', me, key, value);
        }
        return value;
    },

    
    replace: function(key, value) {
        var me = this,
            map = me.map,
            old;

        
        
        if (arguments.length === 1) {
            value = key;
            key = me.getKey(value);
        }

        if (!me.containsKey(key)) {
            me.add(key, value);
        }
        old = map[key];
        map[key] = value;
        me.generation++;
        if (me.hasListeners.replace) {
            me.fireEvent('replace', me, key, value, old);
        }
        return value;
    },

    
    remove: function(o) {
        var key = this.findKey(o);
        if (key !== undefined) {
            return this.removeAtKey(key);
        }
        return false;
    },

    
    removeAtKey: function(key) {
        var me = this,
            value;

        if (me.containsKey(key)) {
            value = me.map[key];
            delete me.map[key];
            --me.length;
            me.generation++;
            if (me.hasListeners.remove) {
                me.fireEvent('remove', me, key, value);
            }
            return true;
        }
        return false;
    },

    
    get: function(key) {
        var map = this.map;
        return map.hasOwnProperty(key) ? map[key] : undefined;
    },

    
    clear: function( initial) {
        var me = this;

        
        if (initial || me.generation) {
            me.map = {};
            me.length = 0;
            me.generation = initial ? 0 : me.generation + 1;
        }
        if (initial !== true && me.hasListeners.clear) {
            me.fireEvent('clear', me);
        }
        return me;
    },

    
    containsKey: function(key) {
        var map = this.map;
        return map.hasOwnProperty(key) && map[key] !== undefined;
    },

    
    contains: function(value) {
        return this.containsKey(this.findKey(value));
    },

    
    getKeys: function() {
        return this.getArray(true);
    },

    
    getValues: function() {
        return this.getArray(false);
    },

    
    getArray: function(isKey) {
        var arr = [],
            key,
            map = this.map;
        for (key in map) {
            if (map.hasOwnProperty(key)) {
                arr.push(isKey ? key: map[key]);
            }
        }
        return arr;
    },

    
    each: function(fn, scope) {
        
        var items = Ext.apply({}, this.map),
            key,
            length = this.length;

        scope = scope || this;
        for (key in items) {
            if (items.hasOwnProperty(key)) {
                if (fn.call(scope, key, items[key], length) === false) {
                    break;
                }
            }
        }
        return this;
    },

    
    clone: function() {
        var hash = new this.self(this.initialConfig),
            map = this.map,
            key;

        hash.suspendEvents();
        for (key in map) {
            if (map.hasOwnProperty(key)) {
                hash.add(key, map[key]);
            }
        }
        hash.resumeEvents();
        return hash;
    },

    
    findKey: function(value) {
        var key,
            map = this.map;

        for (key in map) {
            if (map.hasOwnProperty(key) && map[key] === value) {
                return key;
            }
        }
        return undefined;
    }
});


Ext.define('Ext.AbstractManager', {

    

                                   

    

    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) {
        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];


        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.ComponentManager', {
    extend:  Ext.AbstractManager ,
    alternateClassName: 'Ext.ComponentMgr',
    
    singleton: true,
    
    typeName: 'xtype',
    
    
    create: function(component, defaultType){
        if (typeof component == 'string') {
            return Ext.widget(component);
        }
        if (component.isComponent) {
            return component;
        }
        return Ext.widget(component.xtype || defaultType, component);
    },

    registerType: function(type, cls) {
        this.types[type] = cls;
        cls[this.typeName] = type;
        cls.prototype[this.typeName] = type;
    }
},
function () {
    
    Ext.getCmp = function(id) {
        return Ext.ComponentManager.get(id);
    };
});


Ext.define('Ext.ComponentQuery', {
    singleton: true 
               
                               
                       
     
}, function() {

    var cq = this,
        domQueryOperators = Ext.dom.Query.operators,
        nthRe = /(\d*)n\+?(\d*)/,
        nthRe2 = /\D/,

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

        
        filterByClassName = function(items, className) {
            var result = [],
                i = 0,
                length = items.length,
                candidate;
            for (; i < length; i++) {
                candidate = items[i];
                if (candidate.hasCls(className)) {
                    result.push(candidate);
                }
            }
            return result;
        },

        
        filterByAttribute = function(items, property, operator, compareTo) {
            var result = [],
                i = 0,
                length = items.length,
                mustBeOwnProperty,
                presenceOnly,
                candidate, propValue,
                j, propLen;

            
            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];

                
                if (!mustBeOwnProperty || candidate.hasOwnProperty(property)) {

                    
                    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 (domQueryOperators[operator](Ext.coerce(propValue[j], compareTo), compareTo)) {
                                    result.push(candidate);
                                    break;
                                }
                            }
                        }
                    } else if (!compareTo ? !!candidate[property] : domQueryOperators[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
        }, {
            
            
            
            
            
            
            
            
            re: /^(?:\[((?:[@?$])?[\w\-]*)\s*(?:([\^$*~%!]?=)\s*['"]?(.*?)["']?)?\])/,
            method: filterByAttribute
        }, {
            
            re: /^#([\w\-]+)/,
            method: filterById
        }, {
            
            re: /^\:([\w\-]+)(?:\(((?:\{[^\}]+\})|(?:(?!\{)[^\s>\/]*?(?!\})))\))?/,
            method: filterByPseudo
        }, {
            
            re: /^(?:\{([^\}]+)\})/,
            method: filterFnPattern
        }];

    
    cq.Query = Ext.extend(Object, {
        constructor: function(cfg) {
            cfg = cfg || {};
            Ext.apply(this, cfg);
        },

        
        
        
        
        
        
        
        
        execute : function(root) {
            var operations = this.operations,
                i = 0,
                length = operations.length,
                operation,
                workingItems;

            
            if (!root) {
                workingItems = Ext.ComponentManager.all.getArray();
            }
            
            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,
                len = operations.length,
                active = [component],
                operation, i, j, mode, matches, items, item;
                
            
            for (i = len - 1; i >= 0; --i) {
                operation = operations[i];
                mode = operation.mode;
                
                if (mode) {
                    if (mode === '^') {
                        active = getItems(active, ' ');
                    } else if (mode === '>') {
                        items = [];
                        for (j = 0, len = active.length; j < len; ++j) {
                            item = active[j].getRefOwner();
                            if (item) {
                                items.push(item);
                            }
                        }
                        active = items;
                    } else {
                        active = getAncestors(active);
                    }
                    
                    
                    if (active.length === 0) {
                        return false;
                    }
                    
                } else {
                    active = filterItems(active, operation);
                    if (active.length === 0) {
                        return false;
                    }
                }
            }
            return true;
        },
        
        getMatches: function(components, operations) {
            var len = operations.length,
                i;
                
            for (i = 0; i < len; ++i) {
                components = filterItems(components, operations[i]);
                
                
                if (components.length === 0) {
                    break;
                }  
            }
            return components;
        }
    });

    Ext.apply(this, {

        
        cache: {},

        
        pseudos: {
            not: function(components, selector){
                var CQ = Ext.ComponentQuery,
                    i = 0,
                    length = components.length,
                    results = [],
                    index = -1,
                    component;
                
                for(; i < length; ++i) {
                    component = components[i];
                    if (!CQ.is(component, selector)) {
                        results[++index] = component;
                    }
                }
                return results;
            },
            first: function(components) {
                var ret = [];
                    
                if (components.length > 0) {
                    ret.push(components[0]);
                }
                return ret;       
            },
            last: function(components) {
                var len = components.length,
                    ret = [];
                    
                if (len > 0) {
                    ret.push(components[len - 1]);
                }
                return ret;
            },
            focusable: function(cmps) {
                var len = cmps.length,
                    results = [],
                    i = 0,
                    c;

                for (; i < len; i++) {
                    c = cmps[i];
                    
                    
                    if (c.isFocusable()) {
                        results.push(c);
                    }
                }

                return results;
            },
            "nth-child" : function(c, a) {
                var result = [],
                    m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
                    f = (m[1] || 1) - 0, l = m[2] - 0,
                    i, n, nodeIndex;
                for (i = 0; n = c[i]; i++) {
                    nodeIndex = i + 1;
                    if (f == 1) {
                        if (l == 0 || nodeIndex == l) {
                            result.push(n);
                        }
                    } else if ((nodeIndex + l) % f == 0){
                        result.push(n);
                    }
                }

                return result;
            }
        },

        
        query: function(selector, root) {
            var selectors = selector.split(','),
                length = selectors.length,
                i = 0,
                results = [],
                noDupResults = [], 
                dupMatcher = {}, 
                query, resultsLn, cmp;

            for (; i < length; i++) {
                selector = Ext.String.trim(selectors[i]);
                query = this.cache[selector] || (this.cache[selector] = this.parse(selector));
                results = results.concat(query.execute(root));
            }

            
            
            if (length > 1) {
                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;
        },

        
        is: function(component, selector) {
            if (!selector) {
                return true;
            }
            var selectors = selector.split(','),
                length = selectors.length,
                i = 0,
                query;

            for (; i < length; i++) {
                selector = Ext.String.trim(selectors[i]);
                query = this.cache[selector] || (this.cache[selector] = this.parse(selector));
                if (query.is(component)) {
                    return true;
                }
            }
            return false;
        },

        parse: function(selector) {
            var operations = [],
                length = matchers.length,
                lastSelector,
                tokenMatch,
                matchedChar,
                modeMatch,
                selectorMatch,
                i, matcher, method;

            
            
            
            while (selector && lastSelector !== selector) {
                lastSelector = selector;

                
                tokenMatch = selector.match(tokenRe);

                if (tokenMatch) {
                    matchedChar = tokenMatch[1];

                    
                    if (matchedChar === '#') {
                        operations.push({
                            method: filterById,
                            args: [Ext.String.trim(tokenMatch[2])]
                        });
                    }
                    
                    
                    else if (matchedChar === '.') {
                        operations.push({
                            method: filterByClassName,
                            args: [Ext.String.trim(tokenMatch[2])]
                        });
                    }
                    
                    
                    else {
                        operations.push({
                            method: filterByXType,
                            args: [Ext.String.trim(tokenMatch[2]), Boolean(tokenMatch[3])]
                        });
                    }

                    
                    selector = selector.replace(tokenMatch[0], '');
                }

                
                
                
                while (!(modeMatch = selector.match(modeRe))) {
                    
                    
                    for (i = 0; selector && i < length; i++) {
                        matcher = matchers[i];
                        selectorMatch = selector.match(matcher.re);
                        method = matcher.method;

                        
                        
                        
                        if (selectorMatch) {
                            operations.push({
                                method: Ext.isString(matcher.method)
                                    
                                    
                                    
                                    ? Ext.functionFactory('items', Ext.String.format.apply(Ext.String, [method].concat(selectorMatch.slice(1))))
                                    : matcher.method,
                                args: selectorMatch.slice(1)
                            });
                            selector = selector.replace(selectorMatch[0], '');
                            break; 
                        }
                        
                        if (i === (length - 1)) {
                            Ext.Error.raise('Invalid ComponentQuery selector: "' + arguments[0] + '"');
                        }
                    }
                }

                
                
                
                
                if (modeMatch[1]) { 
                    operations.push({
                        mode: modeMatch[2]||modeMatch[1]
                    });
                    selector = selector.replace(modeMatch[0], '');
                }
            }

            
            
            return new cq.Query({
                operations: operations
            });
        }
    });
});




Ext.define('Ext.util.ProtoElement', (function () {
    var splitWords = Ext.String.splitWords,
        toMap = Ext.Array.toMap;

    return {
        
        isProtoEl: true,
        
        
        clsProp: 'cls',

        
        styleProp: 'style',
        
        
        removedProp: 'removed',

        
        styleIsText: false,

        constructor: function (config) {
            var me = this;

            Ext.apply(me, config);

            me.classList = splitWords(me.cls);
            me.classMap = toMap(me.classList);
            delete me.cls;

            if (Ext.isFunction(me.style)) {
                me.styleFn = me.style;
                delete me.style;
            } else if (typeof me.style == 'string') {
                me.style = Ext.Element.parseStyles(me.style);
            } else if (me.style) {
                me.style = Ext.apply({}, me.style); 
            }
        },
        
        
        flush: function(){
            this.flushClassList = [];
            this.removedClasses = {};
            
            delete this.style;
            delete this.unselectableAttr;
        },

        
        addCls: function (cls) {
            var me = this,
                add = (typeof cls === 'string') ? splitWords(cls) : cls,
                length = add.length,
                list = me.classList,
                map = me.classMap,
                flushList = me.flushClassList,
                i = 0,
                c;

            for (; i < length; ++i) {
                c = add[i];
                if (!map[c]) {
                    map[c] = true;
                    list.push(c);
                    if (flushList) {
                        flushList.push(c);
                        delete me.removedClasses[c];
                    }
                }
            }

            return me;
        },

        
        hasCls: function (cls) {
            return cls in this.classMap;
        },

        
        removeCls: function (cls) {
            var me = this,
                list = me.classList,
                newList = (me.classList = []),
                remove = toMap(splitWords(cls)),
                length = list.length,
                map = me.classMap,
                removedClasses = me.removedClasses,
                i, c;

            for (i = 0; i < length; ++i) {
                c = list[i];
                if (remove[c]) {
                    if (removedClasses) {
                        if (map[c]) {
                            removedClasses[c] = true;
                            Ext.Array.remove(me.flushClassList, c);
                        }
                    }
                    delete map[c];
                } else {
                    newList.push(c);
                }
            }

            return me;
        },

        
        setStyle: function (prop, value) {
            var me = this,
                style = me.style || (me.style = {});

            if (typeof prop == 'string') {
                if (arguments.length === 1) {
                    me.setStyle(Ext.Element.parseStyles(prop));
                } else {
                    style[prop] = value;
                }
            } else {
                Ext.apply(style, prop);
            }

            return me;
        },

        unselectable: function() {
            
            this.addCls(Ext.dom.Element.unselectableCls);

            if (Ext.isOpera) {
                this.unselectableAttr = true;
            }
        },

        
        writeTo: function (to) {
            var me = this,
                classList = me.flushClassList || me.classList,
                removedClasses = me.removedClasses,
                style;

            if (me.styleFn) {
                style = Ext.apply({}, me.styleFn());
                Ext.apply(style, me.style);
            } else {
                style = me.style;
            }

            to[me.clsProp] = classList.join(' ');

            if (style) {
                to[me.styleProp] = me.styleIsText ? Ext.DomHelper.generateStyles(style, null, true) : style;
            }
            
            if (removedClasses) {
                removedClasses = Ext.Object.getKeys(removedClasses);
                if (removedClasses.length) {
                    to[me.removedProp] = removedClasses.join(' ');
                }
            }

            if (me.unselectableAttr) {
                to.unselectable = 'on';
            }

            return to;
        }
    };
}()));


Ext.define('Ext.PluginManager', {
    extend:  Ext.AbstractManager ,
    alternateClassName: 'Ext.PluginMgr',
    singleton: true,
    typeName: 'ptype',

    
    create : function(config, defaultType, host) {
        var result;

        if (config.init) {
            result = config;
        } else {
            
            if (host) {
                config = Ext.apply({}, config); 
                config.cmp = host;
            }
            
            else {
                host = config.cmp;
            }

            if (config.xclass) {
                result = Ext.create(config);
            } else {
                
                result = Ext.ClassManager.getByAlias(('plugin.' + (config.ptype || defaultType)));

                if (typeof result === 'function') {
                    result = new result(config);
                }
            }
        }

        
        if (result && host && result.setCmp && !result.setCmpCalled) {
            result.setCmp(host);
            result.setCmpCalled = true;
        }
        return result;
    },

    
    findByType: function(type, defaultsOnly) {
        var matches = [],
            types   = this.types,
            name,
            item;

        for (name in types) {
            if (!types.hasOwnProperty(name)) {
                continue;
            }
            item = types[name];

            if (item.type == type && (!defaultsOnly || (defaultsOnly === true && item.isDefault))) {
                matches.push(item);
            }
        }

        return matches;
    }
}, function() {
    
    Ext.preg = function() {
        return Ext.PluginManager.registerType.apply(Ext.PluginManager, arguments);
    };
});


Ext.define('Ext.util.Filter', {

    
    

    

    
    id: null,

    
    anyMatch: false,

    
    exactMatch: false,

    
    caseSensitive: false,

    
    disabled: false,

    
    operator: null,

    

    statics: {
        
        createFilterFn: function(filters) {
            return filters && filters.length ? function(candidate) {
                var isMatch = true,
                    length = filters.length,
                    i, filter;

                for (i = 0; isMatch && i < length; i++) {
                    filter = filters[i];

                    
                    if (!filter.disabled) {
                        isMatch = isMatch && filter.filterFn.call(filter.scope || filter, candidate);
                    }
                }
                return isMatch;
            } : function() {
                return true;
            };
        }
    },

    operatorFns: {
        "<": function(candidate) {
            return Ext.coerce(this.getRoot(candidate)[this.property], this.value) < this.value;
        },
        "<=": function(candidate) {
            return Ext.coerce(this.getRoot(candidate)[this.property], this.value) <= this.value;
        },
        "=": function(candidate) {
            return Ext.coerce(this.getRoot(candidate)[this.property], this.value) == this.value;
        },
        ">=": function(candidate) {
            return Ext.coerce(this.getRoot(candidate)[this.property], this.value) >= this.value;
        },
        ">": function(candidate) {
            return Ext.coerce(this.getRoot(candidate)[this.property], this.value) > this.value;
        },
        "!=": function(candidate) {
            return Ext.coerce(this.getRoot(candidate)[this.property], this.value) != this.value;
        }
    },

    
    constructor: function(config) {
        var me = this;
        me.initialConfig = config;
        Ext.apply(me, config);

        
        
        me.filter = me.filter || me.filterFn;

        if (me.filter === undefined) {
            me.setValue(config.value);
        }
    },

    
    setValue: function(value) {
        var me = this;
        me.value = value;
        if (me.property === undefined || me.value === undefined) {
            
            

            
        } else {
            me.filter = me.createFilterFn();
        }

        me.filterFn = me.filter;
    },

    
    setFilterFn: function(filterFn) {
        this.filterFn = this.filter = filterFn;
    },

    
    createFilterFn: function() {
        var me       = this,
            matcher  = me.createValueMatcher(),
            property = me.property;

        if (me.operator) {
            return me.operatorFns[me.operator];
        } else {
            return function(item) {
                var value = me.getRoot(item)[property];
                return matcher === null ? value === null : matcher.test(value);
            };
        }
    },

    
    getRoot: function(item) {
        var root = this.root;
        return root === undefined ? item : item[root];
    },

    
    createValueMatcher : function() {
        var me            = this,
            value         = me.value,
            anyMatch      = me.anyMatch,
            exactMatch    = me.exactMatch,
            caseSensitive = me.caseSensitive,
            escapeRe      = Ext.String.escapeRegex;

        if (value === null) {
            return value;
        }

        if (!value.exec) { 
            value = String(value);

            if (anyMatch === true) {
                value = escapeRe(value);
            } else {
                value = '^' + escapeRe(value);
                if (exactMatch === true) {
                    value += '$';
                }
            }
            value = new RegExp(value, caseSensitive ? '' : 'i');
         }

         return value;
    },

    serialize: function() {
        var me = this,
            result = Ext.apply({}, me.initialConfig);

        result.value = me.value;
        return result;
    }
}, function() {
    
    this.prototype.operatorFns['=='] = this.prototype.operatorFns['='];
});


Ext.define('Ext.util.AbstractMixedCollection', {
                                  

    mixins: {
        observable:  Ext.util.Observable 
    },

    
    isMixedCollection: true,

    
    generation: 0,
    
    
    indexGeneration: 0,
    
    constructor: function(allowFunctions, keyFn) {
        var me = this;

        
        if (arguments.length === 1 && Ext.isObject(allowFunctions)) {
            me.initialConfig = allowFunctions;
            Ext.apply(me, allowFunctions);
        }
        
        else {
            me.allowFunctions = allowFunctions === true;
            if (keyFn) {
                me.getKey = keyFn;
            }
            me.initialConfig = {
                allowFunctions: me.allowFunctions,
                getKey: me.getKey
            };
        }

        me.items = [];
        me.map = {};
        me.keys = [];
        me.indexMap = {};
        me.length = 0;

        

        

        
       
        

        me.mixins.observable.constructor.call(me);
    },

    
    allowFunctions : false,

    
    add : function(key, obj) {
        var len = this.length,
            out;
        
        if (arguments.length === 1) {
            out = this.insert(len, key);
        } else {
            out = this.insert(len, key, obj);
        }
        return out;
    },

    
    getKey : function(o) {
         return o.id;
    },

    
    replace : function(key, o) {
        var me = this,
            old,
            index;

        if (arguments.length == 1) {
            o = arguments[0];
            key = me.getKey(o);
        }
        old = me.map[key];
        if (typeof key == 'undefined' || key === null || typeof old == 'undefined') {
             return me.add(key, o);
        }
        me.generation++;
        index = me.indexOfKey(key);
        me.items[index] = o;
        me.map[key] = o;
        if (me.hasListeners.replace) {
            me.fireEvent('replace', key, old, o);
        }
        return o;
    },
    
    
    updateKey: function(oldKey, newKey) {
        var me = this,
            map = me.map,
            indexMap = me.indexMap,
            index = me.indexOfKey(oldKey),
            item;

        if (index > -1) {
            item = map[oldKey];
            delete map[oldKey];
            delete indexMap[oldKey];
            map[newKey] = item;
            indexMap[newKey] = index;
            me.keys[index] = newKey;

            
            
            me.indexGeneration = ++me.generation;
        }
    },

    
    addAll : function(objs) {
        var me = this,
            key;

        if (arguments.length > 1 || Ext.isArray(objs)) {
            me.insert(me.length, arguments.length > 1 ? arguments : objs);
        } else {
            for (key in objs) {
                if (objs.hasOwnProperty(key)) {
                    if (me.allowFunctions || typeof objs[key] != 'function') {
                        me.add(key, objs[key]);
                    }
                }
            }
        }
    },

    
    each : function(fn, scope){
        var items = Ext.Array.push([], this.items), 
            i = 0,
            len = items.length,
            item;

        for (; i < len; i++) {
            item = items[i];
            if (fn.call(scope || item, item, i, len) === false) {
                break;
            }
        }
    },

    
    eachKey : function(fn, scope){
        var keys = this.keys,
            items = this.items,
            i = 0,
            len = keys.length;

        for (; i < len; i++) {
            fn.call(scope || window, keys[i], items[i], i, len);
        }
    },

    
    findBy : function(fn, scope) {
        var keys = this.keys,
            items = this.items,
            i = 0,
            len = items.length;

        for (; i < len; i++) {
            if (fn.call(scope || window, items[i], keys[i])) {
                return items[i];
            }
        }
        return null;
    },

    
    find : function() {
        if (Ext.isDefined(Ext.global.console)) {
            Ext.global.console.warn('Ext.util.MixedCollection: find has been deprecated. Use findBy instead.');
        }
        return this.findBy.apply(this, arguments);
    },

    
    insert : function(index, key, obj) {
        var out;
        if (Ext.isIterable(key)) {
            out = this.doInsert(index, key, obj);
        } else {
            if (arguments.length > 2) {
                out = this.doInsert(index, [key], [obj]);
            } else {
                out = this.doInsert(index, [key]);
            }
            out = out[0];
        }
        return out;
    },
 
    
    doInsert : function(index, keys, objects) {
        var me = this,
            itemKey,
            removeIndex,
            i, len = keys.length,
            deDupedLen = len,
            fireAdd = me.hasListeners.add,
            syncIndices,
            newKeys = {},
            passedDuplicates,
            oldKeys, oldObjects;

        
        
        if (objects != null) {
            me.useLinearSearch = true;
        }
        
        else {
            objects = keys;
            keys = new Array(len);
            for (i = 0; i < len; i++) {
                keys[i] = this.getKey(objects[i]);
            }
        }

        
        me.suspendEvents();
        for (i = 0; i < len; i++) {
            itemKey = keys[i];

            
            removeIndex = me.indexOfKey(itemKey);
            if (removeIndex !== -1) {
                if (removeIndex < index) {
                    index--;
                }
                me.removeAt(removeIndex);
            }

            if (itemKey != null) {
                
                if (newKeys[itemKey] != null) {
                    passedDuplicates = true;
                    deDupedLen--;
                }
                newKeys[itemKey] = i;
            }
        }
        me.resumeEvents();

        
        if (passedDuplicates) {
            oldKeys = keys;
            oldObjects = objects;
            keys = new Array(deDupedLen);
            objects = new Array(deDupedLen);
            i = 0;

            
            
            for (itemKey in newKeys) {
                keys[i] = oldKeys[newKeys[itemKey]];
                objects[i] = oldObjects[newKeys[itemKey]];
                i++;
            }
            len = deDupedLen;
        }

        
        syncIndices = index === me.length && me.indexGeneration === me.generation;

        
        Ext.Array.insert(me.items, index, objects);
        Ext.Array.insert(me.keys,  index, keys);
        me.length += len;
        me.generation++;
        if (syncIndices) {
            me.indexGeneration = me.generation;
        }
        for (i = 0; i < len; i++, index++) {
            itemKey = keys[i];
            if (itemKey != null) {
                me.map[itemKey] = objects[i];

                
                if (syncIndices) {
                    me.indexMap[itemKey] = index;
                }
            }
            if (fireAdd) {
                me.fireEvent('add', index, objects[i], itemKey);
            }
        }
        return objects;
    },

    
    remove : function(o) {
        var me = this,
            removeKey,
            index;

        
        
        
        
        
        if (!me.useLinearSearch && (removeKey = me.getKey(o))) {
            index = me.indexOfKey(removeKey);
        }

        
        else {
            index = Ext.Array.indexOf(me.items, o);
        }

        return (index === -1) ? false : me.removeAt(index);
    },

    
    removeAll : function(items) {
        var me = this, 
            i;

        if (items || me.hasListeners.remove) {
            
            if (items) {
                for (i = items.length - 1; i >= 0; --i) {
                    me.remove(items[i]);
                }
            } else {
                while (me.length) {
                    me.removeAt(0);
                }
            }
        } else {
            me.length = me.items.length = me.keys.length = 0;
            me.map = {};
            me.indexMap = {};
            me.generation++;
            me.indexGeneration = me.generation;
        }
    },
    
    
    removeAt : function(index) {
        var me = this,
            o,
            key;

        if (index < me.length && index >= 0) {
            me.length--;
            o = me.items[index];
            Ext.Array.erase(me.items, index, 1);
            key = me.keys[index];
            if (typeof key != 'undefined') {
                delete me.map[key];
            }
            Ext.Array.erase(me.keys, index, 1);
            if (me.hasListeners.remove) {
                me.fireEvent('remove', o, key);
            }
            me.generation++;
            return o;
        }
        return false;
    },

    
    removeRange : function(index, removeCount) {
        var me = this,
            o,
            key,
            i,
            limit,
            syncIndices,
            trimming;

        if (index < me.length && index >= 0) {
            if (!removeCount) {
                removeCount = 1;
            }
            limit = Math.min(index + removeCount, me.length);
            removeCount = limit - index;

            
            trimming = limit === me.length;
            syncIndices = trimming && me.indexGeneration === me.generation;

            
            for (i = index; i < limit; i++) {
                key = me.keys[i];
                if (key != null) {
                    delete me.map[key];
                    if (syncIndices) {
                        delete me.indexMap[key];
                    }
                }
            }
            
            o = me.items[i - 1];
            
            me.length -= removeCount;
            me.generation++;
            if (syncIndices) {
                me.indexGeneration = me.generation;
            }

            
            
            
            
            if (trimming) {
                me.items.length = me.keys.length = me.length;
            } else {
                me.items.splice(index, removeCount);
                me.keys.splice(index, removeCount);
            }

            
            return o;
        }
        return false;
    },

    
    removeAtKey : function(key) {
        var me = this,
            keys = me.keys,
            i;

        
        if (key == null) {
            for (i = keys.length - 1; i >=0; i--) {
                if (keys[i] == null) {
                    me.removeAt(i);
                }
            }
        }
        
        else {
            return me.removeAt(me.indexOfKey(key));
        }
    },

    
    getCount : function() {
        return this.length;
    },

    
    indexOf : function(o) {
        var me = this,
            key;

        if (o != null) {
            
            
            
            
            
            if (!me.useLinearSearch && (key = me.getKey(o))) {
                return this.indexOfKey(key);
            }

            
            return Ext.Array.indexOf(me.items, o);
        }

        
        return -1;
    },

    
    indexOfKey : function(key) {
        if (!this.map.hasOwnProperty(key)) {
            return -1;
        }
        if (this.indexGeneration !== this.generation) {
            this.rebuildIndexMap();
        }
        return this.indexMap[key];
    },
    
    rebuildIndexMap: function() {
        var me = this,
            indexMap = me.indexMap = {},
            keys = me.keys,
            len = keys.length,
            i;

        for (i = 0; i < len; i++) {
            indexMap[keys[i]] = i;
        }
        me.indexGeneration = me.generation;
    },

    
    get : function(key) {
        var me = this,
            mk = me.map[key],
            item = mk !== undefined ? mk : (typeof key == 'number') ? me.items[key] : undefined;
        return typeof item != 'function' || me.allowFunctions ? item : null; 
    },

    
    getAt : function(index) {
        return this.items[index];
    },

    
    getByKey : function(key) {
        return this.map[key];
    },

    
    contains : function(o) {
        var me = this,
            key;

        if (o != null) {
            
            
            
            
            
            if (!me.useLinearSearch && (key = me.getKey(o))) {
                return this.map[key] != null;
            }

            
            return Ext.Array.indexOf(this.items, o) !== -1;
        }
        
        return false;
    },

    
    containsKey : function(key) {
        return this.map.hasOwnProperty(key);
    },

    
    clear : function() {
        var me = this;

        
        if (me.generation) {
            me.length = 0;
            me.items = [];
            me.keys = [];
            me.map = {};
            me.indexMap = {};

            me.generation++;
            me.indexGeneration = me.generation;
        }
        if (me.hasListeners.clear) {
            me.fireEvent('clear');
        }
    },

    
    first : function() {
        return this.items[0];
    },

    
    last : function() {
        return this.items[this.length - 1];
    },

    
    sum: function(property, root, start, end) {
        var values = this.extractValues(property, root),
            length = values.length,
            sum    = 0,
            i;

        start = start || 0;
        end   = (end || end === 0) ? end : length - 1;

        for (i = start; i <= end; i++) {
            sum += values[i];
        }

        return sum;
    },

    
    collect: function(property, root, allowNull) {
        var values = this.extractValues(property, root),
            length = values.length,
            hits   = {},
            unique = [],
            value, strValue, i;

        for (i = 0; i < length; i++) {
            value = values[i];
            strValue = String(value);

            if ((allowNull || !Ext.isEmpty(value)) && !hits[strValue]) {
                hits[strValue] = true;
                unique.push(value);
            }
        }

        return unique;
    },

    
    extractValues: function(property, root) {
        var values = this.items;

        if (root) {
            values = Ext.Array.pluck(values, root);
        }

        return Ext.Array.pluck(values, property);
    },

    
    hasRange: function(start, end) {
        return (end < this.length);
    },

    
    getRange : function(start, end){
        var me = this,
            items = me.items,
            range = [],
            len = items.length,
            tmp, reverse;

        if (len < 1) {
            return range;
        }
        
        if (start > end) {
            reverse = true;
            tmp = start;
            start = end;
            end = tmp;
        }

        if (start < 0) {
            start = 0;
        }
        
        if (end == null || end >= len) {
            end = len - 1;    
        }
        
        range = items.slice(start, end + 1);
        if (reverse && range.length) {
            range.reverse();
        }
        return range;
    },

    
    filter : function(property, value, anyMatch, caseSensitive) {
        var filters = [];

        
        if (Ext.isString(property)) {
            filters.push(new Ext.util.Filter({
                property     : property,
                value        : value,
                anyMatch     : anyMatch,
                caseSensitive: caseSensitive
            }));
        } else if (Ext.isArray(property) || property instanceof Ext.util.Filter) {
            filters = filters.concat(property);
        }

        
        
        
        return this.filterBy(Ext.util.Filter.createFilterFn(filters));
    },

    
    filterBy : function(fn, scope) {
        var me = this,
            newMC  = new me.self(me.initialConfig),
            keys   = me.keys,
            items  = me.items,
            length = items.length,
            i;

        newMC.getKey = me.getKey;

        for (i = 0; i < length; i++) {
            if (fn.call(scope || me, items[i], keys[i])) {
                newMC.add(keys[i], items[i]);
            }
        }

        
        
        
        newMC.useLinearSearch = me.useLinearSearch;
        return newMC;
    },

    
    findIndex : function(property, value, start, anyMatch, caseSensitive){
        if(Ext.isEmpty(value, false)){
            return -1;
        }
        value = this.createValueMatcher(value, anyMatch, caseSensitive);
        return this.findIndexBy(function(o){
            return o && value.test(o[property]);
        }, null, start);
    },

    
    findIndexBy : function(fn, scope, start){
        var me = this,
            keys = me.keys,
            items = me.items,
            i = start || 0,
            len = items.length;

        for (; i < len; i++) {
            if (fn.call(scope || me, items[i], keys[i])) {
                return i;
            }
        }
        return -1;
    },

    
    createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) {
        if (!value.exec) { 
            var er = Ext.String.escapeRegex;
            value = String(value);

            if (anyMatch === true) {
                value = er(value);
            } else {
                value = '^' + er(value);
                if (exactMatch === true) {
                    value += '$';
                }
            }
            value = new RegExp(value, caseSensitive ? '' : 'i');
        }
        return value;
    },

    
    clone : function() {
        var me = this,
            copy = new me.self(me.initialConfig);

        copy.add(me.keys, me.items);
        
        
        
        
        copy.useLinearSearch = me.useLinearSearch;
        return copy;
    }
});


Ext.define('Ext.util.Sorter', {

    

    

    

    

    
    direction: "ASC",

    constructor: function(config) {
        var me = this;

        Ext.apply(me, config);


        me.updateSortFunction();
    },

    
    createSortFunction: function(sorterFn) {
        var me        = this,
            direction = me.direction || "ASC",
            modifier  = direction.toUpperCase() == "DESC" ? -1 : 1;

        
        
        return function(o1, o2) {
            return modifier * sorterFn.call(me, o1, o2);
        };
    },

    
    defaultSorterFn: function(o1, o2) {
        var me = this,
            transform = me.transform,
            v1 = me.getRoot(o1)[me.property],
            v2 = me.getRoot(o2)[me.property];

        if (transform) {
            v1 = transform(v1);
            v2 = transform(v2);
        }

        return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
    },

    
    getRoot: function(item) {
        return this.root === undefined ? item : item[this.root];
    },

    
    setDirection: function(direction) {
        var me = this;
        me.direction = direction ? direction.toUpperCase() : direction;
        me.updateSortFunction();
    },

    
    toggle: function() {
        var me = this;
        me.direction = Ext.String.toggle(me.direction, "ASC", "DESC");
        me.updateSortFunction();
    },

    
    updateSortFunction: function(fn) {
        var me = this;
        fn = fn || me.sorterFn || me.defaultSorterFn;
        me.sort = me.createSortFunction(fn);
    },

    serialize: function() {
        return {
            root: this.root,
            property: this.property,
            direction: this.direction
        };
    }
});


Ext.define("Ext.util.Sortable", {
    
    isSortable: true,

    
    defaultSortDirection: "ASC",

               
                         
      

    statics: {
        
        createComparator: function(sorters) {
            return sorters && sorters.length ? function(r1, r2) {
                var result = sorters[0].sort(r1, r2),
                    length = sorters.length,
                    i = 1;

                
                for (; i < length; i++) {
                    result = result || sorters[i].sort.call(this, r1, r2);
                }
                return result;
            }: function() {
                return 0;
            };
        }
    },

    

    

    
    initSortable: function() {
        var me = this,
            sorters = me.sorters;

        
        me.sorters = new Ext.util.AbstractMixedCollection(false, function(item) {
            return item.id || item.property;
        });

        if (sorters) {
            me.sorters.addAll(me.decodeSorters(sorters));
        }
    },

    
    sort: function(sorters, direction, where, doSort) {
        var me = this,
            sorter,
            newSorters;

        if (Ext.isArray(sorters)) {
            doSort = where;
            where = direction;
            newSorters = sorters;
        }
        else if (Ext.isObject(sorters)) {
            doSort = where;
            where = direction;
            newSorters = [sorters];
        }
        else if (Ext.isString(sorters)) {
            sorter = me.sorters.get(sorters);

            if (!sorter) {
                sorter = {
                    property : sorters,
                    direction: direction
                };
                newSorters = [sorter];
            }
            else if (direction === undefined) {
                sorter.toggle();
            }
            else {
                sorter.setDirection(direction);
            }
        }

        if (newSorters && newSorters.length) {
            newSorters = me.decodeSorters(newSorters);
            if (Ext.isString(where)) {
                if (where === 'prepend') {
                    me.sorters.insert(0, newSorters);
                }
                else {
                    me.sorters.addAll(newSorters);
                }
            }
            else {
                me.sorters.clear();
                me.sorters.addAll(newSorters);
            }
        }

        if (doSort !== false) {
            me.fireEvent('beforesort', me, newSorters);
            me.onBeforeSort(newSorters);
            
            sorters = me.sorters.items;
            if (sorters.length) {
                
                me.doSort(me.generateComparator());
            }
        }

        return sorters;
    },

    
    generateComparator: function() {
        var sorters = this.sorters.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,
            fields = this.model ? this.model.prototype.fields : null,
            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 (fields && !config.transform) {
                    field = fields.get(config.property);
                    config.transform = field && field.sortType !== Ext.identityFn ? field.sortType : undefined;
                }
                sorters[i] = new Ext.util.Sorter(config);
            }
        }

        return sorters;
    },

    getSorters: function() {
        return this.sorters.items;
    },
    
    
    getFirstSorter: function(){
        var sorters = this.sorters.items,
            len = sorters.length,
            i = 0,
            sorter;
            
        for (; i < len; ++i) {
            sorter = sorters[i];
            if (!sorter.isGrouper) {
                return sorter;    
            }
        }
        return null;
    }
}, function() {
    
    this.prototype.createComparator = this.createComparator;
});


Ext.define('Ext.util.MixedCollection', {
    extend:  Ext.util.AbstractMixedCollection ,
    mixins: {
        sortable:  Ext.util.Sortable 
    },

    

    
    constructor: function() {
        var me = this;
        me.callParent(arguments);
        me.addEvents('sort');
        me.mixins.sortable.initSortable.call(me);
    },

    doSort: function(sorterFn) {
        this.sortBy(sorterFn);
    },

    
    _sort : function(property, dir, fn) {
        var me = this,
            i, len,
            dsc   = String(dir).toUpperCase() == 'DESC' ? -1 : 1,

            
            c     = [],
            keys  = me.keys,
            items = me.items,
            o;

        
        fn = fn || function(a, b) {
            return a - b;
        };

        
        for (i = 0, len = items.length; i < len; i++) {
            c[c.length] = {
                key  : keys[i],
                value: items[i],
                index: i
            };
        }

        
        Ext.Array.sort(c, function(a, b) {
            return fn(a[property], b[property]) * dsc ||
                
                (a.index < b.index ? -1 : 1);
        });

        
        
        for (i = 0, len = c.length; i < len; i++) {
            o = c[i];
            items[i] = o.value;
            keys[i]  = o.key;
            me.indexMap[o.key] = i;
        }
        me.generation++;
        me.indexGeneration = me.generation;
        me.fireEvent('sort', me);
    },

    
    sortBy: function(sorterFn) {
        var me     = this,
            items  = me.items,
            item,
            keys   = me.keys,
            key,
            length = items.length,
            i;

        
        for (i = 0; i < length; i++) {
            items[i].$extCollectionIndex = i;
        }

        Ext.Array.sort(items, function(a, b) {
            return sorterFn(a, b) ||
                
                (a.$extCollectionIndex < b.$extCollectionIndex ? -1 : 1);
        });

        
        for (i = 0; i < length; i++) {
            item = items[i];
            key = me.getKey(item);
            keys[i] = key;
            me.indexMap[key] = i;
            delete items.$extCollectionIndex;
        }
        me.generation++;
        me.indexGeneration = me.generation;
        me.fireEvent('sort', me, items, keys);
    },

    
    findInsertionIndex: function(newItem, sorterFn) {
        var me    = this,
            items = me.items,
            start = 0,
            end   = items.length - 1,
            middle,
            comparison;

        if (!sorterFn) {
            sorterFn = me.generateComparator();
        }
        while (start <= end) {
            middle = (start + end) >> 1;
            comparison = sorterFn(newItem, items[middle]);
            if (comparison >= 0) {
                start = middle + 1;
            } else if (comparison < 0) {
                end = middle - 1;
            }
        }
        return start;
    },

    
    reorder: function(mapping) {
        var me = this,
            items = me.items,
            index = 0,
            length = items.length,
            order = [],
            remaining = [],
            oldIndex;

        me.suspendEvents();

        
        for (oldIndex in mapping) {
            order[mapping[oldIndex]] = items[oldIndex];
        }

        for (index = 0; index < length; index++) {
            if (mapping[index] == undefined) {
                remaining.push(items[index]);
            }
        }

        for (index = 0; index < length; index++) {
            if (order[index] == undefined) {
                order[index] = remaining.shift();
            }
        }

        me.clear();
        me.addAll(order);

        me.resumeEvents();
        me.fireEvent('sort', me);
    },

    
    sortByKey : function(dir, fn){
        this._sort('key', dir, fn || function(a, b){
            var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();
            return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
        });
    }
});


Ext.define('Ext.fx.target.Target', {

    isAnimTarget: true,

    
    constructor: function(target) {
        this.target = target;
        this.id = this.getId();
    },
    
    getId: function() {
        return this.target.id;
    },
    
    remove: function() {
        Ext.destroy(this.target);
    }
});


Ext.define('Ext.fx.target.Element', {

    
    
    extend:  Ext.fx.target.Target ,
    
    

    type: 'element',

    getElVal: function(el, attr, val) {
        if (val == undefined) {
            if (attr === 'x') {
                val = el.getX();
            } else if (attr === 'y') {
                val = el.getY();
            } else if (attr === 'scrollTop') {
                val = el.getScroll().top;
            } else if (attr === 'scrollLeft') {
                val = el.getScroll().left;
            } else if (attr === 'height') {
                val = el.getHeight();
            } else if (attr === 'width') {
                val = el.getWidth();
            } else {
                val = el.getStyle(attr);
            }
        }
        return val;
    },

    getAttr: function(attr, val) {
        var el = this.target;
        return [[ el, this.getElVal(el, attr, val)]];
    },

    setAttr: function(targetData) {
        var target = this.target,
            ln = targetData.length,
            attrs, attr, o, i, j, ln2;
            
        for (i = 0; i < ln; i++) {
            attrs = targetData[i].attrs;
            for (attr in attrs) {
                if (attrs.hasOwnProperty(attr)) {
                    ln2 = attrs[attr].length;
                    for (j = 0; j < ln2; j++) {
                        o = attrs[attr][j];
                        this.setElVal(o[0], attr, o[1]);
                    }
                }
            }
        }
    },
    
    setElVal: function(element, attr, value){
        if (attr === 'x') {
            element.setX(value);
        } else if (attr === 'y') {
            element.setY(value);
        } else if (attr === 'scrollTop') {
            element.scrollTo('top', value);
        } else if (attr === 'scrollLeft') {
            element.scrollTo('left',value);
        } else if (attr === 'width') {
            element.setWidth(value);
        } else if (attr === 'height') {
            element.setHeight(value);
        } else {
            element.setStyle(attr, value);
        }
    }
});


Ext.define('Ext.fx.target.ElementCSS', {

    

    extend:  Ext.fx.target.Element ,

    

    setAttr: function(targetData, isFirstFrame) {
        var cssArr = {
                attrs: [],
                duration: [],
                easing: []
            },
            ln = targetData.length,
            attributes,
            attrs,
            attr,
            easing,
            duration,
            o,
            i,
            j,
            ln2;
        for (i = 0; i < ln; i++) {
            attrs = targetData[i];
            duration = attrs.duration;
            easing = attrs.easing;
            attrs = attrs.attrs;
            for (attr in attrs) {
                if (Ext.Array.indexOf(cssArr.attrs, attr) == -1) {
                    cssArr.attrs.push(attr.replace(/[A-Z]/g, function(v) {
                        return '-' + v.toLowerCase();
                    }));
                    cssArr.duration.push(duration + 'ms');
                    cssArr.easing.push(easing);
                }
            }
        }
        attributes = cssArr.attrs.join(',');
        duration = cssArr.duration.join(',');
        easing = cssArr.easing.join(', ');
        for (i = 0; i < ln; i++) {
            attrs = targetData[i].attrs;
            for (attr in attrs) {
                ln2 = attrs[attr].length;
                for (j = 0; j < ln2; j++) {
                    o = attrs[attr][j];
                    o[0].setStyle(Ext.supports.CSS3Prefix + 'TransitionProperty', isFirstFrame ? '' : attributes);
                    o[0].setStyle(Ext.supports.CSS3Prefix + 'TransitionDuration', isFirstFrame ? '' : duration);
                    o[0].setStyle(Ext.supports.CSS3Prefix + 'TransitionTimingFunction', isFirstFrame ? '' : easing);
                    o[0].setStyle(attr, o[1]);

                    
                    if (isFirstFrame) {
                        o = o[0].dom.offsetWidth;
                    }
                    else {
                        
                        o[0].on(Ext.supports.CSS3TransitionEnd, function() {
                            this.setStyle(Ext.supports.CSS3Prefix + 'TransitionProperty', null);
                            this.setStyle(Ext.supports.CSS3Prefix + 'TransitionDuration', null);
                            this.setStyle(Ext.supports.CSS3Prefix + 'TransitionTimingFunction', null);
                        }, o[0], { single: true });
                    }
                }
            }
        }
    }
});


Ext.define('Ext.fx.target.CompositeElement', {

    

    extend:  Ext.fx.target.Element ,

    

    
    isComposite: true,
    
    constructor: function(target) {
        target.id = target.id || Ext.id(null, 'ext-composite-');
        this.callParent([target]);
    },

    getAttr: function(attr, val) {
        var out      = [],
            target = this.target,
            elements = target.elements,
            length   = elements.length,
            i,
            el;

        for (i = 0; i < length; i++) {
            el = elements[i];

            if (el) {
                el = target.getElement(el);
                out.push([el, this.getElVal(el, attr, val)]);
            }
        }

        return out;
    },
    
    setAttr: function(targetData){
        var target = this.target,
            ln = targetData.length,
            elements = target.elements,
            ln3 = elements.length,
            value, k,
            attrs, attr, o, i, j, ln2;
            
        for (i = 0; i < ln; i++) {
            attrs = targetData[i].attrs;
            for (attr in attrs) {
                if (attrs.hasOwnProperty(attr)) {
                    ln2 = attrs[attr].length;
                    for (j = 0; j < ln2; j++) {
                        value = attrs[attr][j][1];
                        for (k = 0; k < ln3; ++k) {
                            el = elements[k];
                            if (el) {
                                el = target.getElement(el);
                                this.setElVal(el, attr, value);
                            }
                        }
                    }
                }
            }
        }
    },
    
    remove: function() {
        this.target.remove();
    }
});


Ext.define('Ext.fx.target.CompositeElementCSS', {

    

    extend:  Ext.fx.target.CompositeElement ,

                                           

    
    setAttr: function() {
        return Ext.fx.target.ElementCSS.prototype.setAttr.apply(this, arguments);
    }
});



Ext.define('Ext.fx.target.Sprite', {

    

    extend:  Ext.fx.target.Target ,

    

    type: 'draw',

    getFromPrim: function (sprite, attr) {
        var obj;
        switch (attr) {
            case 'rotate':
            case 'rotation':
                obj = sprite.attr.rotation;
                return {
                    x: obj.x || 0,
                    y: obj.y || 0,
                    degrees: obj.degrees || 0
                };
            case 'scale':
            case 'scaling':
                obj = sprite.attr.scaling;
                return {
                    x: obj.x || 1,
                    y: obj.y || 1,
                    cx: obj.cx || 0,
                    cy: obj.cy || 0
                };
            case 'translate':
            case 'translation':
                obj = sprite.attr.translation;
                return {
                    x: obj.x || 0,
                    y: obj.y || 0
                };
            default:
                return sprite.attr[attr];
        }
    },

    getAttr: function (attr, val) {
        return [
            [this.target, val != undefined ? val : this.getFromPrim(this.target, attr)]
        ];
    },

    setAttr: function (targetData) {
        var ln = targetData.length,
            spriteArr = [],
            attrsConf, attr, attrArr, attrs, sprite, idx, value, i, j, x, y, ln2;
        for (i = 0; i < ln; i++) {
            attrsConf = targetData[i].attrs;
            for (attr in attrsConf) {
                attrArr = attrsConf[attr];
                ln2 = attrArr.length;
                for (j = 0; j < ln2; j++) {
                    sprite = attrArr[j][0];
                    attrs = attrArr[j][1];
                    if (attr === 'translate' || attr === 'translation') {
                        value = {
                            x: attrs.x,
                            y: attrs.y
                        };
                    }
                    else if (attr === 'rotate' || attr === 'rotation') {
                        x = attrs.x;
                        if (isNaN(x)) {
                            x = null;
                        }
                        y = attrs.y;
                        if (isNaN(y)) {
                            y = null;
                        }
                        value = {
                            degrees: attrs.degrees,
                            x: x,
                            y: y
                        };
                    } else if (attr === 'scale' || attr === 'scaling') {
                        x = attrs.x;
                        if (isNaN(x)) {
                            x = null;
                        }
                        y = attrs.y;
                        if (isNaN(y)) {
                            y = null;
                        }
                        value = {
                            x: x,
                            y: y,
                            cx: attrs.cx,
                            cy: attrs.cy
                        };
                    }
                    else if (attr === 'width' || attr === 'height' || attr === 'x' || attr === 'y') {
                        value = parseFloat(attrs);
                    }
                    else {
                        value = attrs;
                    }
                    idx = Ext.Array.indexOf(spriteArr, sprite);
                    if (idx == -1) {
                        spriteArr.push([sprite, {}]);
                        idx = spriteArr.length - 1;
                    }
                    spriteArr[idx][1][attr] = value;
                }
            }
        }
        ln = spriteArr.length;
        for (i = 0; i < ln; i++) {
            spriteArr[i][0].setAttributes(spriteArr[i][1]);
        }
        this.target.redraw();
    }
});



Ext.define('Ext.fx.target.CompositeSprite', {

    

    extend:  Ext.fx.target.Sprite ,

    

    getAttr: function(attr, val) {
        var out     = [],
            sprites = [].concat(this.target.items),
            length  = sprites.length,
            i,
            sprite;

        for (i = 0; i < length; i++) {
            sprite = sprites[i];
            out.push([sprite, val != undefined ? val : this.getFromPrim(sprite, attr)]);
        }

        return out;
    }
});


Ext.define('Ext.fx.target.Component', {

    
   
    extend:  Ext.fx.target.Target ,
    
    

    type: 'component',

    
    getPropMethod: {
        top: function() {
            return this.getPosition(true)[1];
        },
        left: function() {
            return this.getPosition(true)[0];
        },
        x: function() {
            return this.getPosition()[0];
        },
        y: function() {
            return this.getPosition()[1];
        },
        height: function() {
            return this.getHeight();
        },
        width: function() {
            return this.getWidth();
        },
        opacity: function() {
            return this.el.getStyle('opacity');
        }
    },

    setMethods: {
        top:     'setPosition',
        left:    'setPosition',
        x:       'setPagePosition',
        y:       'setPagePosition',
        height:  'setSize',
        width:   'setSize',
        opacity: 'setOpacity'
    },

    
    getAttr: function(attr, val) {
        return [[this.target, val !== undefined ? val : this.getPropMethod[attr].call(this.target)]];
    },

    setAttr: function(targetData, isFirstFrame, isLastFrame) {
        var me = this,
            ln = targetData.length,
            attrs, attr, o, i, j, targets, left, top, w, h,
            methodsToCall = {},
            methodProps;

        for (i = 0; i < ln; i++) {
            attrs = targetData[i].attrs;
            for (attr in attrs) {
                targets = attrs[attr].length;
                for (j = 0; j < targets; j++) {
                    o = attrs[attr][j];
                    methodProps = methodsToCall[me.setMethods[attr]] || (methodsToCall[me.setMethods[attr]] = {});
                    methodProps.target = o[0];
                    methodProps[attr] = o[1];
                    
                }
            }
            if (methodsToCall.setPosition) {
                o = methodsToCall.setPosition;
                left = (o.left === undefined) ? undefined : parseFloat(o.left);
                top = (o.top === undefined) ? undefined : parseFloat(o.top);
                o.target.setPosition(left, top);
            }
            if (methodsToCall.setPagePosition) {
                o = methodsToCall.setPagePosition;
                o.target.setPagePosition(o.x, o.y);
            }
            if (methodsToCall.setSize) {
                o = methodsToCall.setSize;
                
                w = (o.width === undefined) ? o.target.getWidth() : parseFloat(o.width);
                h = (o.height === undefined) ? o.target.getHeight() : parseFloat(o.height);

                
                
                
                
                
                
                
                o.target.el.setSize(w, h);
                if (isLastFrame || me.dynamic) {
                    
                    
                    
                    Ext.globalEvents.on({
                        idle: Ext.Function.bind(o.target.setSize, o.target, [w, h]),
                        single: true
                    });
                }
            }
            if (methodsToCall.setOpacity) {
                o = methodsToCall.setOpacity;
                o.target.el.setStyle('opacity', o.opacity);
            }
        }
    }
});



Ext.define('Ext.fx.Queue', {

                                   

    constructor: function() {
        this.targets = new Ext.util.HashMap();
        this.fxQueue = {};
    },

    
    getFxDefaults: function(targetId) {
        var target = this.targets.get(targetId);
        if (target) {
            return target.fxDefaults;
        }
        return {};
    },

    
    setFxDefaults: function(targetId, obj) {
        var target = this.targets.get(targetId);
        if (target) {
            target.fxDefaults = Ext.apply(target.fxDefaults || {}, obj);
        }
    },

    
    stopAnimation: function(targetId) {
        var me = this,
            queue = me.getFxQueue(targetId),
            ln = queue.length;
        while (ln) {
            queue[ln - 1].end();
            ln--;
        }
    },

    
    getActiveAnimation: function(targetId) {
        var queue = this.getFxQueue(targetId);
        return (queue && !!queue.length) ? queue[0] : false;
    },

    
    hasFxBlock: function(targetId) {
        var queue = this.getFxQueue(targetId);
        return queue && queue[0] && queue[0].block;
    },

    
    getFxQueue: function(targetId) {
        if (!targetId) {
            return false;
        }
        var me = this,
            queue = me.fxQueue[targetId],
            target = me.targets.get(targetId);

        if (!target) {
            return false;
        }

        if (!queue) {
            me.fxQueue[targetId] = [];
            
            if (target.type != 'element') {
                target.target.on('destroy', function() {
                    me.fxQueue[targetId] = [];
                });
            }
        }
        return me.fxQueue[targetId];
    },

    
    queueFx: function(anim) {
        var me = this,
            target = anim.target,
            queue, ln;

        if (!target) {
            return;
        }

        queue = me.getFxQueue(target.getId());
        ln = queue.length;

        if (ln) {
            if (anim.concurrent) {
                anim.paused = false;
            }
            else {
                queue[ln - 1].on('afteranimate', function() {
                    anim.paused = false;
                });
            }
        }
        else {
            anim.paused = false;
        }
        anim.on('afteranimate', function() {
            Ext.Array.remove(queue, anim);
            if (queue.length === 0) {
                me.targets.remove(anim.target);
            }
            if (anim.remove) {
                if (target.type == 'element') {
                    var el = Ext.get(target.id);
                    if (el) {
                        el.remove();
                    }
                }
            }
        }, me, {
            single: true
        });
        queue.push(anim);
    }
});



Ext.define('Ext.fx.Manager', {

    

    singleton: true,

                                          
                                       
                                          
                                                
                                                   
                                      
                                               
                                          

    mixins: {
        queue:  Ext.fx.Queue 
    },

    

    constructor: function() {
        var me = this;
        me.items = new Ext.util.MixedCollection();
        me.targetArr = {};
        me.mixins.queue.constructor.call(me);
        
        
        
        me.taskRunner = new Ext.util.TaskRunner();

        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
    },

    
    interval: 16,

    
    forceJS: true,

    
    createTarget: function(target) {
        var me = this,
            useCSS3 = !me.forceJS && Ext.supports.Transitions,
            targetObj;

        me.useCSS3 = useCSS3;

        if (target) {
            
            if (target.tagName || Ext.isString(target) || target.isFly) {
                target = Ext.get(target);
                targetObj = new Ext.fx.target['Element' + (useCSS3 ? 'CSS' : '')](target);
            }
            
            else if (target.dom) {
                targetObj = new Ext.fx.target['Element' + (useCSS3 ? 'CSS' : '')](target);
            }
            
            else if (target.isComposite) {
                targetObj = new Ext.fx.target['CompositeElement' + (useCSS3 ? 'CSS' : '')](target);
            }
            
            else if (target.isSprite) {
                targetObj = new Ext.fx.target.Sprite(target);
            }
            
            else if (target.isCompositeSprite) {
                targetObj = new Ext.fx.target.CompositeSprite(target);
            }
            
            else if (target.isComponent) {
                targetObj = new Ext.fx.target.Component(target);
            }
            else if (target.isAnimTarget) {
                return target;
            }
            else {
                return null;
            }
            me.targets.add(targetObj);
            return targetObj;
        }
        else {
            return null;
        }
    },

    
    addAnim: function(anim) {
        var me = this,
            items = me.items,
            task = me.task;

        
        
        
        items.add(anim.id, anim);
        

        
        if (!task && items.length) {
            task = me.task = {
                run: me.runner,
                interval: me.interval,
                scope: me
            };
            
            me.taskRunner.start(task);
        }
    },

    
    removeAnim: function(anim) {
        var me = this,
            items = me.items,
            task = me.task;
                
        items.removeAtKey(anim.id);
        
        
        
        if (task && !items.length) {
            
            me.taskRunner.stop(task);
            delete me.task;
        }
    },

    
    runner: function() {
        var me = this,
            items = me.items.getRange(),
            i = 0,
            len = items.length,
            anim;

        
        me.targetArr = {};

        
        me.timestamp = new Date();
        
        
        
        
        
        
        
        
        
        
        
        for (; i < len; i++) {
            anim = items[i];
            
            if (anim.isReady()) {
                
                me.startAnim(anim);
            }
        }
        
        for (i = 0; i < len; i++) {
            anim = items[i];
            
            if (anim.isRunning()) {
                
                me.runAnim(anim);
            }
        }

        
        me.applyPendingAttrs();
    },

    
    startAnim: function(anim) {
        anim.start(this.timestamp);
    },

    
    runAnim: function(anim, forceEnd) {
        if (!anim) {
            return;
        }
        var me = this,
            useCSS3 = me.useCSS3 && anim.target.type == 'element',
            elapsedTime = me.timestamp - anim.startTime,
            lastFrame = (elapsedTime >= anim.duration),
            target, o;
            
        if (forceEnd) {
            elapsedTime = anim.duration;
            lastFrame = true;
        }

        target = this.collectTargetData(anim, elapsedTime, useCSS3, lastFrame);
        
        
        
        if (useCSS3) {
            
            
            
            anim.target.setAttr(target.anims[anim.id].attributes, true);

            
            me.collectTargetData(anim, anim.duration, useCSS3, lastFrame);

            
            anim.paused = true;

            target = anim.target.target;
            
            if (anim.target.isComposite) {
                target = anim.target.target.last();
            }

            
            o = {};
            o[Ext.supports.CSS3TransitionEnd] = anim.lastFrame;
            o.scope = anim;
            o.single = true;
            target.on(o);
        }
        return target;
    },
    
    jumpToEnd: function(anim) {
        var target = this.runAnim(anim, true);
        this.applyAnimAttrs(target, target.anims[anim.id]);
    },

    
    collectTargetData: function(anim, elapsedTime, useCSS3, isLastFrame) {
        var targetId = anim.target.getId(),
            target = this.targetArr[targetId];
        
        if (!target) {
            
            
            
            
            target = this.targetArr[targetId] = {
                id: targetId,
                el: anim.target,
                anims: {}
            };
        }

        
        
        
        
        target.anims[anim.id] = {
            id: anim.id,
            anim: anim,
            elapsed: elapsedTime,
            isLastFrame: isLastFrame,
            
            attributes: [{
                duration: anim.duration,
                easing: (useCSS3 && anim.reverse) ? anim.easingFn.reverse().toCSS3() : anim.easing,
                
                
                attrs: anim.runAnim(elapsedTime)
            }]
        };
        
        return target;
    },
    
    
    
    
    applyAnimAttrs: function(target, animWrap) {
        var anim = animWrap.anim;
        if (animWrap.attributes && anim.isRunning()) {
            target.el.setAttr(animWrap.attributes, false, animWrap.isLastFrame);
                            
            
            if (animWrap.isLastFrame) {
                anim.lastFrame();
            }
        }
    },
    
    
    applyPendingAttrs: function() {
        var targetArr = this.targetArr,
            target, targetId, animWrap, anim, animId;
        
        
        for (targetId in targetArr) {
            if (targetArr.hasOwnProperty(targetId)) {
                target = targetArr[targetId];
                
                
                for (animId in target.anims) {
                    if (target.anims.hasOwnProperty(animId)) {
                        animWrap = target.anims[animId];
                        anim = animWrap.anim;
                        
                        
                        if (animWrap.attributes && anim.isRunning()) {
                            
                            target.el.setAttr(animWrap.attributes, false, animWrap.isLastFrame);
                            
                            
                            if (animWrap.isLastFrame) {
                                
                                anim.lastFrame();
                            }
                        }
                    }
                }
            }
        }
    }
});


Ext.define('Ext.fx.Animator', {

    

    mixins: {
        observable:  Ext.util.Observable 
    },

                                 

    

    
    isAnimator: true,

    
    duration: 250,

    
    delay: 0,

    
    delayStart: 0,

    
    dynamic: false,

    
    easing: 'ease',

    
    running: false,

    
    paused: false,

    
    damper: 1,

    
    iterations: 1,

    
    currentIteration: 0,

    
    keyframeStep: 0,

    
    animKeyFramesRE: /^(from|to|\d+%?)$/,

    

     
    constructor: function(config) {
        var me = this;
        config = Ext.apply(me, config || {});
        me.config = config;
        me.id = Ext.id(null, 'ext-animator-');
        me.addEvents(
            
            'beforeanimate',
            
            'keyframe',
            
            'afteranimate'
        );
        me.mixins.observable.constructor.call(me, config);
        me.timeline = [];
        me.createTimeline(me.keyframes);
        if (me.target) {
            me.applyAnimator(me.target);
            Ext.fx.Manager.addAnim(me);
        }
    },

    
    sorter: function (a, b) {
        return a.pct - b.pct;
    },

    
    createTimeline: function(keyframes) {
        var me = this,
            attrs = [],
            to = me.to || {},
            duration = me.duration,
            prevMs, ms, i, ln, pct, attr;

        for (pct in keyframes) {
            if (keyframes.hasOwnProperty(pct) && me.animKeyFramesRE.test(pct)) {
                attr = {attrs: Ext.apply(keyframes[pct], to)};
                
                if (pct == "from") {
                    pct = 0;
                }
                else if (pct == "to") {
                    pct = 100;
                }
                
                attr.pct = parseInt(pct, 10);
                attrs.push(attr);
            }
        }
        
        Ext.Array.sort(attrs, me.sorter);
        
        
        
        

        ln = attrs.length;
        for (i = 0; i < ln; i++) {
            prevMs = (attrs[i - 1]) ? duration * (attrs[i - 1].pct / 100) : 0;
            ms = duration * (attrs[i].pct / 100);
            me.timeline.push({
                duration: ms - prevMs,
                attrs: attrs[i].attrs
            });
        }
    },

    
    applyAnimator: function(target) {
        var me = this,
            anims = [],
            timeline = me.timeline,
            ln = timeline.length,
            anim, easing, damper, attrs, i;

        if (me.fireEvent('beforeanimate', me) !== false) {
            for (i = 0; i < ln; i++) {
                anim = timeline[i];
                attrs = anim.attrs;
                easing = attrs.easing || me.easing;
                damper = attrs.damper || me.damper;
                delete attrs.easing;
                delete attrs.damper;
                anim = new Ext.fx.Anim({
                    target: target,
                    easing: easing,
                    damper: damper,
                    duration: anim.duration,
                    paused: true,
                    to: attrs
                });
                anims.push(anim);
            }
            me.animations = anims;
            me.target = anim.target;
            for (i = 0; i < ln - 1; i++) {
                anim = anims[i];
                anim.nextAnim = anims[i + 1];
                anim.on('afteranimate', function() {
                    this.nextAnim.paused = false;
                });
                anim.on('afteranimate', function() {
                    this.fireEvent('keyframe', this, ++this.keyframeStep);
                }, me);
            }
            anims[ln - 1].on('afteranimate', function() {
                this.lastFrame();
            }, me);
        }
    },

    
    start: function(startTime) {
        var me = this,
            delay = me.delay,
            delayStart = me.delayStart,
            delayDelta;
        if (delay) {
            if (!delayStart) {
                me.delayStart = startTime;
                return;
            }
            else {
                delayDelta = startTime - delayStart;
                if (delayDelta < delay) {
                    return;
                }
                else {
                    
                    startTime = new Date(delayStart.getTime() + delay);
                }
            }
        }
        if (me.fireEvent('beforeanimate', me) !== false) {
            me.startTime = startTime;
            me.running = true;
            me.animations[me.keyframeStep].paused = false;
        }
    },

    
    lastFrame: function() {
        var me = this,
            iter = me.iterations,
            iterCount = me.currentIteration;

        iterCount++;
        if (iterCount < iter) {
            me.startTime = new Date();
            me.currentIteration = iterCount;
            me.keyframeStep = 0;
            me.applyAnimator(me.target);
            me.animations[me.keyframeStep].paused = false;
        }
        else {
            me.currentIteration = 0;
            me.end();
        }
    },

    
    end: function() {
        var me = this;
        me.fireEvent('afteranimate', me, me.startTime, new Date() - me.startTime);
    },
    
    isReady: function() {
        return this.paused === false && this.running === false && this.iterations > 0;
    },
    
    isRunning: function() {
        
        return false;
    }
});


Ext.define('Ext.fx.CubicBezier', {

    

    singleton: true,

    

    cubicBezierAtTime: function(t, p1x, p1y, p2x, p2y, duration) {
        var cx = 3 * p1x,
            bx = 3 * (p2x - p1x) - cx,
            ax = 1 - cx - bx,
            cy = 3 * p1y,
            by = 3 * (p2y - p1y) - cy,
            ay = 1 - cy - by;
        function sampleCurveX(t) {
            return ((ax * t + bx) * t + cx) * t;
        }
        function solve(x, epsilon) {
            var t = solveCurveX(x, epsilon);
            return ((ay * t + by) * t + cy) * t;
        }
        function solveCurveX(x, epsilon) {
            var t0, t1, t2, x2, d2, i;
            for (t2 = x, i = 0; i < 8; i++) {
                x2 = sampleCurveX(t2) - x;
                if (Math.abs(x2) < epsilon) {
                    return t2;
                }
                d2 = (3 * ax * t2 + 2 * bx) * t2 + cx;
                if (Math.abs(d2) < 1e-6) {
                    break;
                }
                t2 = t2 - x2 / d2;
            }
            t0 = 0;
            t1 = 1;
            t2 = x;
            if (t2 < t0) {
                return t0;
            }
            if (t2 > t1) {
                return t1;
            }
            while (t0 < t1) {
                x2 = sampleCurveX(t2);
                if (Math.abs(x2 - x) < epsilon) {
                    return t2;
                }
                if (x > x2) {
                    t0 = t2;
                } else {
                    t1 = t2;
                }
                t2 = (t1 - t0) / 2 + t0;
            }
            return t2;
        }
        return solve(t, 1 / (200 * duration));
    },

    cubicBezier: function(x1, y1, x2, y2) {
        var fn = function(pos) {
            return Ext.fx.CubicBezier.cubicBezierAtTime(pos, x1, y1, x2, y2, 1);
        };
        fn.toCSS3 = function() {
            return 'cubic-bezier(' + [x1, y1, x2, y2].join(',') + ')';
        };
        fn.reverse = function() {
            return Ext.fx.CubicBezier.cubicBezier(1 - x2, 1 - y2, 1 - x1, 1 - y1);
        };
        return fn;
    }
});




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


Ext.define('Ext.draw.Color', {

    

    

    colorToHexRe: /(.*?)rgb\((\d+),\s*(\d+),\s*(\d+)\)/,
    rgbRe: /\s*rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)\s*/,
    hexRe: /\s*#([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)\s*/,

    
    lightnessFactor: 0.2,

    
    constructor : function(red, green, blue) {
        var me = this,
            clamp = Ext.Number.constrain;
        me.r = clamp(red, 0, 255);
        me.g = clamp(green, 0, 255);
        me.b = clamp(blue, 0, 255);
    },

    
    getRed: function() {
        return this.r;
    },

    
    getGreen: function() {
        return this.g;
    },

    
    getBlue: function() {
        return this.b;
    },

    
    getRGB: function() {
        var me = this;
        return [me.r, me.g, me.b];
    },

    
    getHSL: function() {
        var me = this,
            r = me.r / 255,
            g = me.g / 255,
            b = me.b / 255,
            max = Math.max(r, g, b),
            min = Math.min(r, g, b),
            delta = max - min,
            h,
            s = 0,
            l = 0.5 * (max + min);

        
        if (min != max) {
            s = (l < 0.5) ? delta / (max + min) : delta / (2 - max - min);
            if (r == max) {
                h = 60 * (g - b) / delta;
            } else if (g == max) {
                h = 120 + 60 * (b - r) / delta;
            } else {
                h = 240 + 60 * (r - g) / delta;
            }
            if (h < 0) {
                h += 360;
            }
            if (h >= 360) {
                h -= 360;
            }
        }
        return [h, s, l];
    },

    
    getLighter: function(factor) {
        var hsl = this.getHSL();
        factor = factor || this.lightnessFactor;
        hsl[2] = Ext.Number.constrain(hsl[2] + factor, 0, 1);
        return this.fromHSL(hsl[0], hsl[1], hsl[2]);
    },

    
    getDarker: function(factor) {
        factor = factor || this.lightnessFactor;
        return this.getLighter(-factor);
    },

    
    toString: function() {
        var me = this,
            round = Math.round,
            r = round(me.r).toString(16),
            g = round(me.g).toString(16),
            b = round(me.b).toString(16);
        r = (r.length == 1) ? '0' + r : r;
        g = (g.length == 1) ? '0' + g : g;
        b = (b.length == 1) ? '0' + b : b;
        return ['#', r, g, b].join('');
    },

    
    toHex: function(color) {
        if (Ext.isArray(color)) {
            color = color[0];
        }
        if (!Ext.isString(color)) {
            return '';
        }
        if (color.substr(0, 1) === '#') {
            return color;
        }
        var digits = this.colorToHexRe.exec(color),
            red,
            green,
            blue,
            rgb;

        if (Ext.isArray(digits)) {
            red = parseInt(digits[2], 10);
            green = parseInt(digits[3], 10);
            blue = parseInt(digits[4], 10);
            rgb = blue | (green << 8) | (red << 16);
            return digits[1] + '#' + ("000000" + rgb.toString(16)).slice(-6);
        }
        else {
            return color;
        }
    },

    
    fromString: function(str) {
        var values, r, g, b,
            parse = parseInt,
            firstChar = str.substr(0, 1),
            colorValue;

        if (firstChar != '#') {
            colorValue = Ext.draw.Color.cssColors[str];
            if (colorValue) {
                str = colorValue;
                firstChar = str.substr(0, 1);
            }
        }

        if ((str.length == 4 || str.length == 7) && firstChar === '#') {
            values = str.match(this.hexRe);
            if (values) {
                r = parse(values[1], 16) >> 0;
                g = parse(values[2], 16) >> 0;
                b = parse(values[3], 16) >> 0;
                if (str.length == 4) {
                    r += (r * 16);
                    g += (g * 16);
                    b += (b * 16);
                }
            }
        }
        else {
            values = str.match(this.rgbRe);
            if (values) {
                r = values[1];
                g = values[2];
                b = values[3];
            }
        }

        return (typeof r == 'undefined') ? undefined : new Ext.draw.Color(r, g, b);
    },

    
    getGrayscale: function() {
        
        return this.r * 0.3 + this.g * 0.59 + this.b * 0.11;
    },

    
    fromHSL: function(h, s, l) {
        var C, X, m, i, rgb = [],
            abs = Math.abs,
            floor = Math.floor;

        if (s == 0 || h == null) {
            
            rgb = [l, l, l];
        }
        else {
            
            
            
            
            h /= 60;
            C = s * (1 - abs(2 * l - 1));
            X = C * (1 - abs(h - 2 * floor(h / 2) - 1));
            m = l - C / 2;
            switch (floor(h)) {
                case 0:
                    rgb = [C, X, 0];
                    break;
                case 1:
                    rgb = [X, C, 0];
                    break;
                case 2:
                    rgb = [0, C, X];
                    break;
                case 3:
                    rgb = [0, X, C];
                    break;
                case 4:
                    rgb = [X, 0, C];
                    break;
                case 5:
                    rgb = [C, 0, X];
                    break;
            }
            rgb = [rgb[0] + m, rgb[1] + m, rgb[2] + m];
        }
        return new Ext.draw.Color(rgb[0] * 255, rgb[1] * 255, rgb[2] * 255);
    }
}, function() {
    var prototype = this.prototype;

    this.addStatics({
        
        fromHSL: function() {
            return prototype.fromHSL.apply(prototype, arguments);
        },
        fromString: function() {
            return prototype.fromString.apply(prototype, arguments);
        },
        toHex: function() {
            return prototype.toHex.apply(prototype, arguments);
        },
        
        cssColors: {
            aliceblue: '#F0F8FF',
            antiquewhite: '#FAEBD7',
            aqua: '#00FFFF',
            aquamarine: '#7FFFD4',
            azure: '#F0FFFF',
            beige: '#F5F5DC',
            bisque: '#FFE4C4',
            black: '#000000',
            blanchedalmond: '#FFEBCD',
            blue: '#0000FF',
            blueviolet: '#8A2BE2',
            brown: '#A52A2A',
            burlywood: '#DEB887',
            cadetblue: '#5F9EA0',
            chartreuse: '#7FFF00',
            chocolate: '#D2691E',
            coral: '#FF7F50',
            cornflowerblue: '#6495ED',
            cornsilk: '#FFF8DC',
            crimson: '#DC143C',
            cyan: '#00FFFF',
            darkblue: '#00008B',
            darkcyan: '#008B8B',
            darkgoldenrod: '#B8860B',
            darkgray: '#A9A9A9',
            darkgreen: '#006400',
            darkgrey: '#A9A9A9',
            darkkhaki: '#BDB76B',
            darkmagenta: '#8B008B',
            darkolivegreen: '#556B2F',
            darkorange: '#FF8C00',
            darkorchid: '#9932CC',
            darkred: '#8B0000',
            darksalmon: '#E9967A',
            darkseagreen: '#8FBC8F',
            darkslateblue: '#483D8B',
            darkslategray: '#2F4F4F',
            darkslategrey: '#2F4F4F',
            darkturquoise: '#00CED1',
            darkviolet: '#9400D3',
            deeppink: '#FF1493',
            deepskyblue: '#00BFFF',
            dimgray: '#696969',
            dimgrey: '#696969',
            dodgerblue: '#1E90FF',
            firebrick: '#B22222',
            floralwhite: '#FFFAF0',
            forestgreen: '#228B22',
            fuchsia: '#FF00FF',
            gainsboro: '#DCDCDC',
            ghostwhite: '#F8F8FF',
            gold: '#FFD700',
            goldenrod: '#DAA520',
            gray: '#808080',
            grey: '#808080',
            green: '#008000',
            greenyellow: '#ADFF2F',
            honeydew: '#F0FFF0',
            hotpink: '#FF69B4',
            indianred: '#CD5C5C',
            indigo: '#4B0082',
            ivory: '#FFFFF0',
            khaki: '#F0E68C',
            lavender: '#E6E6FA',
            lavenderblush: '#FFF0F5',
            lawngreen: '#7CFC00',
            lemonchiffon: '#FFFACD',
            lightblue: '#ADD8E6',
            lightcoral: '#F08080',
            lightcyan: '#E0FFFF',
            lightgoldenrodyellow: '#FAFAD2',
            lightgray: '#D3D3D3',
            lightgreen: '#90EE90',
            lightgrey: '#D3D3D3',
            lightpink: '#FFB6C1',
            lightsalmon: '#FFA07A',
            lightseagreen: '#20B2AA',
            lightskyblue: '#87CEFA',
            lightslategray: '#778899',
            lightslategrey: '#778899',
            lightsteelblue: '#B0C4DE',
            lightyellow: '#FFFFE0',
            lime: '#00FF00',
            limegreen: '#32CD32',
            linen: '#FAF0E6',
            magenta: '#FF00FF',
            maroon: '#800000',
            mediumaquamarine: '#66CDAA',
            mediumblue: '#0000CD',
            mediumorchid: '#BA55D3',
            mediumpurple: '#9370DB',
            mediumseagreen: '#3CB371',
            mediumslateblue: '#7B68EE',
            mediumspringgreen: '#00FA9A',
            mediumturquoise: '#48D1CC',
            mediumvioletred: '#C71585',
            midnightblue: '#191970',
            mintcream: '#F5FFFA',
            mistyrose: '#FFE4E1',
            moccasin: '#FFE4B5',
            navajowhite: '#FFDEAD',
            navy: '#000080',
            oldlace: '#FDF5E6',
            olive: '#808000',
            olivedrab: '#6B8E23',
            orange: '#FFA500',
            orangered: '#FF4500',
            orchid: '#DA70D6',
            palegoldenrod: '#EEE8AA',
            palegreen: '#98FB98',
            paleturquoise: '#AFEEEE',
            palevioletred: '#DB7093',
            papayawhip: '#FFEFD5',
            peachpuff: '#FFDAB9',
            peru: '#CD853F',
            pink: '#FFC0CB',
            plum: '#DDA0DD',
            powderblue: '#B0E0E6',
            purple: '#800080',
            red: '#FF0000',
            rosybrown: '#BC8F8F',
            royalblue: '#4169E1',
            saddlebrown: '#8B4513',
            salmon: '#FA8072',
            sandybrown: '#F4A460',
            seagreen: '#2E8B57',
            seashell: '#FFF5EE',
            sienna: '#A0522D',
            silver: '#C0C0C0',
            skyblue: '#87CEEB',
            slateblue: '#6A5ACD',
            slategray: '#708090',
            slategrey: '#708090',
            snow: '#FFFAFA',
            springgreen: '#00FF7F',
            steelblue: '#4682B4',
            tan: '#D2B48C',
            teal: '#008080',
            thistle: '#D8BFD8',
            tomato: '#FF6347',
            turquoise: '#40E0D0',
            violet: '#EE82EE',
            wheat: '#F5DEB3',
            white: '#FFFFFF',
            whitesmoke: '#F5F5F5',
            yellow: '#FFFF00',
            yellowgreen: '#9ACD32'
        }
    });

});


Ext.define('Ext.draw.Draw', {
    

    singleton: true,

                                 

    

    pathToStringRE: /,?([achlmqrstvxz]),?/gi,
    pathCommandRE: /([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig,
    pathValuesRE: /(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig,
    stopsRE: /^(\d+%?)$/,
    radian: Math.PI / 180,

    availableAnimAttrs: {
        along: "along",
        blur: null,
        "clip-rect": "csv",
        cx: null,
        cy: null,
        fill: "color",
        "fill-opacity": null,
        "font-size": null,
        height: null,
        opacity: null,
        path: "path",
        r: null,
        rotation: "csv",
        rx: null,
        ry: null,
        scale: "csv",
        stroke: "color",
        "stroke-opacity": null,
        "stroke-width": null,
        translation: "csv",
        width: null,
        x: null,
        y: null
    },

    is: function(o, type) {
        type = String(type).toLowerCase();
        return (type == "object" && o === Object(o)) ||
            (type == "undefined" && typeof o == type) ||
            (type == "null" && o === null) ||
            (type == "array" && Array.isArray && Array.isArray(o)) ||
            (Object.prototype.toString.call(o).toLowerCase().slice(8, -1)) == type;
    },

    ellipsePath: function(sprite) {
        var attr = sprite.attr;
        return Ext.String.format("M{0},{1}A{2},{3},0,1,1,{0},{4}A{2},{3},0,1,1,{0},{1}z", attr.x, attr.y - attr.ry, attr.rx, attr.ry, attr.y + attr.ry);
    },

    rectPath: function(sprite) {
        var attr = sprite.attr;
        if (attr.radius) {
            return Ext.String.format("M{0},{1}l{2},0a{3},{3},0,0,1,{3},{3}l0,{5}a{3},{3},0,0,1,{4},{3}l{6},0a{3},{3},0,0,1,{4},{4}l0,{7}a{3},{3},0,0,1,{3},{4}z", attr.x + attr.radius, attr.y, attr.width - attr.radius * 2, attr.radius, -attr.radius, attr.height - attr.radius * 2, attr.radius * 2 - attr.width, attr.radius * 2 - attr.height);
        }
        else {
            return Ext.String.format("M{0},{1}L{2},{1},{2},{3},{0},{3}z", attr.x, attr.y, attr.width + attr.x, attr.height + attr.y);
        }
    },

    
    path2string: function () {
        return this.join(",").replace(Ext.draw.Draw.pathToStringRE, "$1");
    },

    
    pathToString: function(arrayPath) {
        return arrayPath.join(",").replace(Ext.draw.Draw.pathToStringRE, "$1");
    },

    parsePathString: function (pathString) {
        if (!pathString) {
            return null;
        }
        var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0},
            data = [],
            me = this;
        if (me.is(pathString, "array") && me.is(pathString[0], "array")) { 
            data = me.pathClone(pathString);
        }
        if (!data.length) {
            String(pathString).replace(me.pathCommandRE, function (a, b, c) {
                var params = [],
                    name = b.toLowerCase();
                c.replace(me.pathValuesRE, function (a, b) {
                    b && params.push(+b);
                });
                if (name == "m" && params.length > 2) {
                    data.push([b].concat(Ext.Array.splice(params, 0, 2)));
                    name = "l";
                    b = (b == "m") ? "l" : "L";
                }
                while (params.length >= paramCounts[name]) {
                    data.push([b].concat(Ext.Array.splice(params, 0, paramCounts[name])));
                    if (!paramCounts[name]) {
                        break;
                    }
                }
            });
        }
        data.toString = me.path2string;
        return data;
    },

    mapPath: function (path, matrix) {
        if (!matrix) {
            return path;
        }
        var x, y, i, ii, j, jj, pathi;
        path = this.path2curve(path);
        for (i = 0, ii = path.length; i < ii; i++) {
            pathi = path[i];
            for (j = 1, jj = pathi.length; j < jj-1; j += 2) {
                x = matrix.x(pathi[j], pathi[j + 1]);
                y = matrix.y(pathi[j], pathi[j + 1]);
                pathi[j] = x;
                pathi[j + 1] = y;
            }
        }
        return path;
    },

    pathClone: function(pathArray) {
        var res = [],
            j, jj, i, ii;
        if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) { 
            pathArray = this.parsePathString(pathArray);
        }
        for (i = 0, ii = pathArray.length; i < ii; i++) {
            res[i] = [];
            for (j = 0, jj = pathArray[i].length; j < jj; j++) {
                res[i][j] = pathArray[i][j];
            }
        }
        res.toString = this.path2string;
        return res;
    },

    pathToAbsolute: function (pathArray) {
        if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) { 
            pathArray = this.parsePathString(pathArray);
        }
        var res = [],
            x = 0,
            y = 0,
            mx = 0,
            my = 0,
            i = 0,
            ln = pathArray.length,
            r, pathSegment, j, ln2;
        
        if (ln && pathArray[0][0] == "M") {
            x = +pathArray[0][1];
            y = +pathArray[0][2];
            mx = x;
            my = y;
            i++;
            res[0] = ["M", x, y];
        }
        for (; i < ln; i++) {
            r = res[i] = [];
            pathSegment = pathArray[i];
            if (pathSegment[0] != pathSegment[0].toUpperCase()) {
                r[0] = pathSegment[0].toUpperCase();
                switch (r[0]) {
                    
                    case "A":
                        r[1] = pathSegment[1];
                        r[2] = pathSegment[2];
                        r[3] = pathSegment[3];
                        r[4] = pathSegment[4];
                        r[5] = pathSegment[5];
                        r[6] = +(pathSegment[6] + x);
                        r[7] = +(pathSegment[7] + y);
                        break;
                    
                    case "V":
                        r[1] = +pathSegment[1] + y;
                        break;
                    
                    case "H":
                        r[1] = +pathSegment[1] + x;
                        break;
                    case "M":
                    
                        mx = +pathSegment[1] + x;
                        my = +pathSegment[2] + y;
                    default:
                        j = 1;
                        ln2 = pathSegment.length;
                        for (; j < ln2; j++) {
                            r[j] = +pathSegment[j] + ((j % 2) ? x : y);
                        }
                }
            }
            else {
                j = 0;
                ln2 = pathSegment.length;
                for (; j < ln2; j++) {
                    res[i][j] = pathSegment[j];
                }
            }
            switch (r[0]) {
                
                case "Z":
                    x = mx;
                    y = my;
                    break;
                
                case "H":
                    x = r[1];
                    break;
                
                case "V":
                    y = r[1];
                    break;
                
                case "M":
                    pathSegment = res[i];
                    ln2 = pathSegment.length;
                    mx = pathSegment[ln2 - 2];
                    my = pathSegment[ln2 - 1];
                default:
                    pathSegment = res[i];
                    ln2 = pathSegment.length;
                    x = pathSegment[ln2 - 2];
                    y = pathSegment[ln2 - 1];
            }
        }
        res.toString = this.path2string;
        return res;
    },

    
    pathToRelative: function (pathArray) {
        if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) {
            pathArray = this.parsePathString(pathArray);
        }
        var res = [],
            x = 0,
            y = 0,
            mx = 0,
            my = 0,
            start = 0,
            r,
            pa,
            i,
            j,
            k,
            len,
            ii,
            jj,
            kk;
        
        if (pathArray[0][0] == "M") {
            x = pathArray[0][1];
            y = pathArray[0][2];
            mx = x;
            my = y;
            start++;
            res.push(["M", x, y]);
        }
        for (i = start, ii = pathArray.length; i < ii; i++) {
            r = res[i] = [];
            pa = pathArray[i];
            if (pa[0] != pa[0].toLowerCase()) {
                r[0] = pa[0].toLowerCase();
                switch (r[0]) {
                    case "a":
                        r[1] = pa[1];
                        r[2] = pa[2];
                        r[3] = pa[3];
                        r[4] = pa[4];
                        r[5] = pa[5];
                        r[6] = +(pa[6] - x).toFixed(3);
                        r[7] = +(pa[7] - y).toFixed(3);
                        break;
                    case "v":
                        r[1] = +(pa[1] - y).toFixed(3);
                        break;
                    case "m":
                        mx = pa[1];
                        my = pa[2];
                    default:
                        for (j = 1, jj = pa.length; j < jj; j++) {
                            r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3);
                        }
                }
            } else {
                r = res[i] = [];
                if (pa[0] == "m") {
                    mx = pa[1] + x;
                    my = pa[2] + y;
                }
                for (k = 0, kk = pa.length; k < kk; k++) {
                    res[i][k] = pa[k];
                }
            }
            len = res[i].length;
            switch (res[i][0]) {
                case "z":
                    x = mx;
                    y = my;
                    break;
                case "h":
                    x += +res[i][len - 1];
                    break;
                case "v":
                    y += +res[i][len - 1];
                    break;
                default:
                    x += +res[i][len - 2];
                    y += +res[i][len - 1];
            }
        }
        res.toString = this.path2string;
        return res;
    },

    
    path2curve: function (path) {
        var me = this,
            points = me.pathToAbsolute(path),
            ln = points.length,
            attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
            i, seg, segLn, point;
            
        for (i = 0; i < ln; i++) {
            points[i] = me.command2curve(points[i], attrs);
            if (points[i].length > 7) {
                    points[i].shift();
                    point = points[i];
                    while (point.length) {
                        Ext.Array.splice(points, i++, 0, ["C"].concat(Ext.Array.splice(point, 0, 6)));
                    }
                    Ext.Array.erase(points, i, 1);
                    ln = points.length;
                    i--;
                }
            seg = points[i];
            segLn = seg.length;
            attrs.x = seg[segLn - 2];
            attrs.y = seg[segLn - 1];
            attrs.bx = parseFloat(seg[segLn - 4]) || attrs.x;
            attrs.by = parseFloat(seg[segLn - 3]) || attrs.y;
        }
        return points;
    },
    
    interpolatePaths: function (path, path2) {
        var me = this,
            p = me.pathToAbsolute(path),
            p2 = me.pathToAbsolute(path2),
            attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
            attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
            fixArc = function (pp, i) {
                if (pp[i].length > 7) {
                    pp[i].shift();
                    var pi = pp[i];
                    while (pi.length) {
                        Ext.Array.splice(pp, i++, 0, ["C"].concat(Ext.Array.splice(pi, 0, 6)));
                    }
                    Ext.Array.erase(pp, i, 1);
                    ii = Math.max(p.length, p2.length || 0);
                }
            },
            fixM = function (path1, path2, a1, a2, i) {
                if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") {
                    Ext.Array.splice(path2, i, 0, ["M", a2.x, a2.y]);
                    a1.bx = 0;
                    a1.by = 0;
                    a1.x = path1[i][1];
                    a1.y = path1[i][2];
                    ii = Math.max(p.length, p2.length || 0);
                }
            },
            i, ii,
            seg, seg2, seglen, seg2len;
        for (i = 0, ii = Math.max(p.length, p2.length || 0); i < ii; i++) {
            p[i] = me.command2curve(p[i], attrs);
            fixArc(p, i);
            (p2[i] = me.command2curve(p2[i], attrs2));
            fixArc(p2, i);
            fixM(p, p2, attrs, attrs2, i);
            fixM(p2, p, attrs2, attrs, i);
            seg = p[i];
            seg2 = p2[i];
            seglen = seg.length;
            seg2len = seg2.length;
            attrs.x = seg[seglen - 2];
            attrs.y = seg[seglen - 1];
            attrs.bx = parseFloat(seg[seglen - 4]) || attrs.x;
            attrs.by = parseFloat(seg[seglen - 3]) || attrs.y;
            attrs2.bx = (parseFloat(seg2[seg2len - 4]) || attrs2.x);
            attrs2.by = (parseFloat(seg2[seg2len - 3]) || attrs2.y);
            attrs2.x = seg2[seg2len - 2];
            attrs2.y = seg2[seg2len - 1];
        }
        return [p, p2];
    },
    
    
    command2curve: function (pathCommand, d) {
        var me = this;
        if (!pathCommand) {
            return ["C", d.x, d.y, d.x, d.y, d.x, d.y];
        }
        if (pathCommand[0] != "T" && pathCommand[0] != "Q") {
            d.qx = d.qy = null;
        }
        switch (pathCommand[0]) {
            case "M":
                d.X = pathCommand[1];
                d.Y = pathCommand[2];
                break;
            case "A":
                pathCommand = ["C"].concat(me.arc2curve.apply(me, [d.x, d.y].concat(pathCommand.slice(1))));
                break;
            case "S":
                pathCommand = ["C", d.x + (d.x - (d.bx || d.x)), d.y + (d.y - (d.by || d.y))].concat(pathCommand.slice(1));
                break;
            case "T":
                d.qx = d.x + (d.x - (d.qx || d.x));
                d.qy = d.y + (d.y - (d.qy || d.y));
                pathCommand = ["C"].concat(me.quadratic2curve(d.x, d.y, d.qx, d.qy, pathCommand[1], pathCommand[2]));
                break;
            case "Q":
                d.qx = pathCommand[1];
                d.qy = pathCommand[2];
                pathCommand = ["C"].concat(me.quadratic2curve(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[3], pathCommand[4]));
                break;
            case "L":
                pathCommand = ["C"].concat(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[1], pathCommand[2]);
                break;
            case "H":
                pathCommand = ["C"].concat(d.x, d.y, pathCommand[1], d.y, pathCommand[1], d.y);
                break;
            case "V":
                pathCommand = ["C"].concat(d.x, d.y, d.x, pathCommand[1], d.x, pathCommand[1]);
                break;
            case "Z":
                pathCommand = ["C"].concat(d.x, d.y, d.X, d.Y, d.X, d.Y);
                break;
        }
        return pathCommand;
    },

    quadratic2curve: function (x1, y1, ax, ay, x2, y2) {
        var _13 = 1 / 3,
            _23 = 2 / 3;
        return [
                _13 * x1 + _23 * ax,
                _13 * y1 + _23 * ay,
                _13 * x2 + _23 * ax,
                _13 * y2 + _23 * ay,
                x2,
                y2
            ];
    },
    
    rotate: function (x, y, rad) {
        var cos = Math.cos(rad),
            sin = Math.sin(rad),
            X = x * cos - y * sin,
            Y = x * sin + y * cos;
        return {x: X, y: Y};
    },

    arc2curve: function (x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
        
        
        var me = this,
            PI = Math.PI,
            radian = me.radian,
            _120 = PI * 120 / 180,
            rad = radian * (+angle || 0),
            res = [],
            math = Math,
            mcos = math.cos,
            msin = math.sin,
            msqrt = math.sqrt,
            mabs = math.abs,
            masin = math.asin,
            xy, x, y, h, rx2, ry2, k, cx, cy, f1, f2, df, c1, s1, c2, s2,
            t, hx, hy, m1, m2, m3, m4, newres, i, ln, f2old, x2old, y2old;
        if (!recursive) {
            xy = me.rotate(x1, y1, -rad);
            x1 = xy.x;
            y1 = xy.y;
            xy = me.rotate(x2, y2, -rad);
            x2 = xy.x;
            y2 = xy.y;
            x = (x1 - x2) / 2;
            y = (y1 - y2) / 2;
            h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
            if (h > 1) {
                h = msqrt(h);
                rx = h * rx;
                ry = h * ry;
            }
            rx2 = rx * rx;
            ry2 = ry * ry;
            k = (large_arc_flag == sweep_flag ? -1 : 1) *
                    msqrt(mabs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x)));
            cx = k * rx * y / ry + (x1 + x2) / 2;
            cy = k * -ry * x / rx + (y1 + y2) / 2;
            f1 = masin(((y1 - cy) / ry).toFixed(7));
            f2 = masin(((y2 - cy) / ry).toFixed(7));

            f1 = x1 < cx ? PI - f1 : f1;
            f2 = x2 < cx ? PI - f2 : f2;
            if (f1 < 0) {
                f1 = PI * 2 + f1;
            }
            if (f2 < 0) {
                f2 = PI * 2 + f2;
            }
            if (sweep_flag && f1 > f2) {
                f1 = f1 - PI * 2;
            }
            if (!sweep_flag && f2 > f1) {
                f2 = f2 - PI * 2;
            }
        }
        else {
            f1 = recursive[0];
            f2 = recursive[1];
            cx = recursive[2];
            cy = recursive[3];
        }
        df = f2 - f1;
        if (mabs(df) > _120) {
            f2old = f2;
            x2old = x2;
            y2old = y2;
            f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
            x2 = cx + rx * mcos(f2);
            y2 = cy + ry * msin(f2);
            res = me.arc2curve(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
        }
        df = f2 - f1;
        c1 = mcos(f1);
        s1 = msin(f1);
        c2 = mcos(f2);
        s2 = msin(f2);
        t = math.tan(df / 4);
        hx = 4 / 3 * rx * t;
        hy = 4 / 3 * ry * t;
        m1 = [x1, y1];
        m2 = [x1 + hx * s1, y1 - hy * c1];
        m3 = [x2 + hx * s2, y2 - hy * c2];
        m4 = [x2, y2];
        m2[0] = 2 * m1[0] - m2[0];
        m2[1] = 2 * m1[1] - m2[1];
        if (recursive) {
            return [m2, m3, m4].concat(res);
        }
        else {
            res = [m2, m3, m4].concat(res).join().split(",");
            newres = [];
            ln = res.length;
            for (i = 0;  i < ln; i++) {
                newres[i] = i % 2 ? me.rotate(res[i - 1], res[i], rad).y : me.rotate(res[i], res[i + 1], rad).x;
            }
            return newres;
        }
    },

    
    rotateAndTranslatePath: function (sprite) {
        var alpha = sprite.rotation.degrees,
            cx = sprite.rotation.x,
            cy = sprite.rotation.y,
            dx = sprite.translation.x,
            dy = sprite.translation.y,
            path,
            i,
            p,
            xy,
            j,
            res = [];
        if (!alpha && !dx && !dy) {
            return this.pathToAbsolute(sprite.attr.path);
        }
        dx = dx || 0;
        dy = dy || 0;
        path = this.pathToAbsolute(sprite.attr.path);
        for (i = path.length; i--;) {
            p = res[i] = path[i].slice();
            if (p[0] == "A") {
                xy = this.rotatePoint(p[6], p[7], alpha, cx, cy);
                p[6] = xy.x + dx;
                p[7] = xy.y + dy;
            } else {
                j = 1;
                while (p[j + 1] != null) {
                    xy = this.rotatePoint(p[j], p[j + 1], alpha, cx, cy);
                    p[j] = xy.x + dx;
                    p[j + 1] = xy.y + dy;
                    j += 2;
                }
            }
        }
        return res;
    },

    
    rotatePoint: function (x, y, alpha, cx, cy) {
        if (!alpha) {
            return {
                x: x,
                y: y
            };
        }
        cx = cx || 0;
        cy = cy || 0;
        x = x - cx;
        y = y - cy;
        alpha = alpha * this.radian;
        var cos = Math.cos(alpha),
            sin = Math.sin(alpha);
        return {
            x: x * cos - y * sin + cx,
            y: x * sin + y * cos + cy
        };
    },

    pathDimensions: function (path) {
        if (!path || !(path + "")) {
            return {x: 0, y: 0, width: 0, height: 0};
        }
        path = this.path2curve(path);
        var x = 0, 
            y = 0,
            X = [],
            Y = [],
            i = 0,
            ln = path.length,
            p, xmin, ymin, xmax, ymax, dim;
        for (; i < ln; i++) {
            p = path[i];
            if (p[0] == "M") {
                x = p[1];
                y = p[2];
                X.push(x);
                Y.push(y);
            }
            else {
                dim = this.curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
                X = X.concat(dim.min.x, dim.max.x);
                Y = Y.concat(dim.min.y, dim.max.y);
                x = p[5];
                y = p[6];
            }
        }
        xmin = Math.min.apply(0, X);
        ymin = Math.min.apply(0, Y);
        xmax = Math.max.apply(0, X);
        ymax = Math.max.apply(0, Y);
        return {
            x: Math.round(xmin),
            y: Math.round(ymin),
            path: path,
            width: Math.round(xmax - xmin),
            height: Math.round(ymax - ymin)
        };
    },

    intersectInside: function(path, cp1, cp2) {
        return (cp2[0] - cp1[0]) * (path[1] - cp1[1]) > (cp2[1] - cp1[1]) * (path[0] - cp1[0]);
    },

    intersectIntersection: function(s, e, cp1, cp2) {
        var p = [],
            dcx = cp1[0] - cp2[0],
            dcy = cp1[1] - cp2[1],
            dpx = s[0] - e[0],
            dpy = s[1] - e[1],
            n1 = cp1[0] * cp2[1] - cp1[1] * cp2[0],
            n2 = s[0] * e[1] - s[1] * e[0],
            n3 = 1 / (dcx * dpy - dcy * dpx);

        p[0] = (n1 * dpx - n2 * dcx) * n3;
        p[1] = (n1 * dpy - n2 * dcy) * n3;
        return p;
    },

    intersect: function(subjectPolygon, clipPolygon) {
        var me = this,
            i = 0,
            ln = clipPolygon.length,
            cp1 = clipPolygon[ln - 1],
            outputList = subjectPolygon,
            cp2, s, e, ln2, inputList, j;
        for (; i < ln; ++i) {
            cp2 = clipPolygon[i];
            inputList = outputList;
            outputList = [];
            s = inputList[inputList.length - 1];
            j = 0;
            ln2 = inputList.length;
            for (; j < ln2; j++) {
                e = inputList[j];
                if (me.intersectInside(e, cp1, cp2)) {
                    if (!me.intersectInside(s, cp1, cp2)) {
                        outputList.push(me.intersectIntersection(s, e, cp1, cp2));
                    }
                    outputList.push(e);
                }
                else if (me.intersectInside(s, cp1, cp2)) {
                    outputList.push(me.intersectIntersection(s, e, cp1, cp2));
                }
                s = e;
            }
            cp1 = cp2;
        }
        return outputList;
    },
    
    bezier : function (a, b, c, d, x) {
        if (x === 0) {
            return a;
        } 
        else if (x === 1) {
            return d;
        }
        var du = 1 - x,
            d3 = du * du * du,
            r = x / du;
        return d3 * (a + r * (3 * b + r * (3 * c + d * r)));
    },
    
    bezierDim : function (a, b, c, d) {
        var points = [], r,
            A, top, C, delta, bottom, s,
            min, max, i;
        
        if (a + 3 * c == d + 3 * b) {   
            r = a - b;
            r /= 2 * (a - b - b + c);
            if ( r < 1 && r > 0) {
                points.push(r);
            }
        } else {
            
            
            A = a - 3 * b + 3 * c - d;
            top = 2 * (a - b - b + c);
            C = a - b;
            delta = top * top - 4 * A * C;
            bottom = A + A;
            if (delta === 0) {
                r = top / bottom;
                if (r < 1 && r > 0) {
                    points.push(r);
                }
            } else if (delta > 0) {
                s = Math.sqrt(delta);
                r = (s + top) / bottom;
                
                if (r < 1 && r > 0) {
                    points.push(r);
                }
                
                r = (top - s) / bottom;
                
                if (r < 1 && r > 0) {
                    points.push(r);
                }
            }
        }
        min = Math.min(a, d);
        max = Math.max(a, d);
        for (i = 0; i < points.length; i++) {
            min = Math.min(min, this.bezier(a, b, c, d, points[i]));
            max = Math.max(max, this.bezier(a, b, c, d, points[i]));
        }
        return [min, max];
    },
    
    curveDim: function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
        var x = this.bezierDim(p1x, c1x, c2x, p2x),
            y = this.bezierDim(p1y, c1y, c2y, p2y);
        return {
            min: {
                x: x[0],
                y: y[0]
            },
            max: {
                x: x[1],
                y: y[1]
            }
        };
    },

    
    getAnchors: function (prevX, prevY, curX, curY, nextX, nextY, value) {
        value = value || 4;
        var M = Math,
            PI = M.PI,
            halfPI = PI / 2,
            abs = M.abs,
            sin = M.sin,
            cos = M.cos,
            atan = M.atan,
            control1Length, control2Length, control1Angle, control2Angle,
            control1X, control1Y, control2X, control2Y, alpha;

        
        
        control1Length = (curX - prevX) / value;
        control2Length = (nextX - curX) / value;

        
        
        
        
        if ((curY >= prevY && curY >= nextY) || (curY <= prevY && curY <= nextY)) {
            control1Angle = control2Angle = halfPI;
        } else {
            control1Angle = atan((curX - prevX) / abs(curY - prevY));
            if (prevY < curY) {
                control1Angle = PI - control1Angle;
            }
            control2Angle = atan((nextX - curX) / abs(curY - nextY));
            if (nextY < curY) {
                control2Angle = PI - control2Angle;
            }
        }

        
        alpha = halfPI - ((control1Angle + control2Angle) % (PI * 2)) / 2;
        if (alpha > halfPI) {
            alpha -= PI;
        }
        control1Angle += alpha;
        control2Angle += alpha;

        
        control1X = curX - control1Length * sin(control1Angle);
        control1Y = curY + control1Length * cos(control1Angle);
        control2X = curX + control2Length * sin(control2Angle);
        control2Y = curY + control2Length * cos(control2Angle);

        
        
        
        
        if ((curY > prevY && control1Y < prevY) || (curY < prevY && control1Y > prevY)) {
            control1X += abs(prevY - control1Y) * (control1X - curX) / (control1Y - curY);
            control1Y = prevY;
        }
        if ((curY > nextY && control2Y < nextY) || (curY < nextY && control2Y > nextY)) {
            control2X -= abs(nextY - control2Y) * (control2X - curX) / (control2Y - curY);
            control2Y = nextY;
        }
        
        return {
            x1: control1X,
            y1: control1Y,
            x2: control2X,
            y2: control2Y
        };
    },

    
    smooth: function (originalPath, value) {
        var path = this.path2curve(originalPath),
            newp = [path[0]],
            x = path[0][1],
            y = path[0][2],
            j,
            points,
            i = 1,
            ii = path.length,
            beg = 1,
            mx = x,
            my = y,
            pathi,
            pathil,
            pathim,
            pathiml,
            pathip,
            pathipl,
            begl;
        
        for (; i < ii; i++) {
            pathi = path[i];
            pathil = pathi.length;
            pathim = path[i - 1];
            pathiml = pathim.length;
            pathip = path[i + 1];
            pathipl = pathip && pathip.length;
            if (pathi[0] == "M") {
                mx = pathi[1];
                my = pathi[2];
                j = i + 1;
                while (path[j][0] != "C") {
                    j++;
                }
                newp.push(["M", mx, my]);
                beg = newp.length;
                x = mx;
                y = my;
                continue;
            }
            if (pathi[pathil - 2] == mx && pathi[pathil - 1] == my && (!pathip || pathip[0] == "M")) {
                begl = newp[beg].length;
                points = this.getAnchors(pathim[pathiml - 2], pathim[pathiml - 1], mx, my, newp[beg][begl - 2], newp[beg][begl - 1], value);
                newp[beg][1] = points.x2;
                newp[beg][2] = points.y2;
            }
            else if (!pathip || pathip[0] == "M") {
                points = {
                    x1: pathi[pathil - 2],
                    y1: pathi[pathil - 1]
                };
            } else {
                points = this.getAnchors(pathim[pathiml - 2], pathim[pathiml - 1], pathi[pathil - 2], pathi[pathil - 1], pathip[pathipl - 2], pathip[pathipl - 1], value);
            }
            newp.push(["C", x, y, points.x1, points.y1, pathi[pathil - 2], pathi[pathil - 1]]);
            x = points.x2;
            y = points.y2;
        }
        return newp;
    },

    findDotAtSegment: function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
        var t1 = 1 - t;
        return {
            x: Math.pow(t1, 3) * p1x + Math.pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + Math.pow(t, 3) * p2x,
            y: Math.pow(t1, 3) * p1y + Math.pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + Math.pow(t, 3) * p2y
        };
    },

    
    snapEnds: function (from, to, stepsMax, prettyNumbers) {
        if (Ext.isDate(from)) {
            return this.snapEndsByDate(from, to, stepsMax);
        }
        var step = (to - from) / stepsMax,
            level = Math.floor(Math.log(step) / Math.LN10) + 1,
            m = Math.pow(10, level),
            cur,
            floor,
            modulo = Math.round((step % m) * Math.pow(10, 2 - level)),
            interval = [[0, 15], [10, 1], [20, 4], [25, 2], [50, 9], [100, 15]],
            stepCount = 0,
            value,
            weight,
            i,
            topValue,
            topWeight = 1e9,
            ln = interval.length;

        floor = Math.floor(from / m) * m;
        if (from == floor && floor > 0) {
            floor = Math.floor((from - (m/10)) / m) * m;
        }
        
        if (prettyNumbers) {
            for (i = 0; i < ln; i++) {
                value = interval[i][0];
                weight = (value - modulo) < 0 ? 1e6 : (value - modulo) / interval[i][1];
                if (weight < topWeight) {
                    topValue = value;
                    topWeight = weight;
                }
            }
            step = Math.floor(step * Math.pow(10, -level)) * Math.pow(10, level) + topValue * Math.pow(10, level - 2);

            if (from < 0 && to >= 0) {
                cur = 0;
                while (cur > from) {
                    cur -= step;
                    stepCount++;
                }
                from = +cur.toFixed(10);

                cur = 0;
                while (cur < to) {
                    cur += step;
                    stepCount++;
                }
                to = +cur.toFixed(10);
            } else {
                cur = from = floor;
                while (cur < to) {
                    cur += step;
                    stepCount++;
                }
            }
            to = +cur.toFixed(10);
        } else {
            from = floor;
            stepCount = stepsMax;
        }
        
        return {
            from: from,
            to: to,
            power: level,
            step: step,
            steps: stepCount
        };
    },

    
    snapEndsByDate: function (from, to, stepsMax, lockEnds) {
        var selectedStep = false,
            scales       = [
                [Ext.Date.MILLI, [1, 2, 5, 10, 20, 50, 100, 200, 250, 500]],
                [Ext.Date.SECOND, [1, 2, 5, 10, 15, 30]],
                [Ext.Date.MINUTE, [1, 2, 5, 10, 15, 30]],
                [Ext.Date.HOUR, [1, 2, 3, 4, 6, 12]],
                [Ext.Date.DAY, [1, 2, 7, 14]],
                [Ext.Date.MONTH, [1, 2, 3, 6]]
            ],
            sLen         = scales.length,
            stop         = false,
            scale, j, yearDiff, s;

        
        for (s = 0; s < sLen; s++) {
            scale = scales[s];
            if (!stop) {
                for (j = 0; j < scale[1].length; j++) {
                    if (to < Ext.Date.add(from, scale[0], scale[1][j] * stepsMax)) {
                        selectedStep = [scale[0], scale[1][j]];
                        stop         = true;
                        break;
                    }
                }
            }
        }

        if (!selectedStep) {
            yearDiff = this.snapEnds(from.getFullYear(), to.getFullYear() + 1, stepsMax, lockEnds);
            selectedStep = [Date.YEAR, Math.round(yearDiff.step)];
        }
        return this.snapEndsByDateAndStep(from, to, selectedStep, lockEnds);
    },


    

    snapEndsByDateAndStep: function(from, to, step, lockEnds) {
        var fromStat = [from.getFullYear(), from.getMonth(), from.getDate(),
            from.getHours(), from.getMinutes(), from.getSeconds(), from.getMilliseconds()],
            testFrom, testTo, date, year, month, day, fractionalMonth, stepsArray,
            stepUnit = step[0], stepValue = step[1],
            steps = 0;
        
        if (lockEnds) {
            testFrom = from;
        }
        else {
            switch (stepUnit) {
                case Ext.Date.MILLI:
                    testFrom = new Date(fromStat[0], fromStat[1], fromStat[2], fromStat[3],
                            fromStat[4], fromStat[5], Math.floor(fromStat[6] / stepValue) * stepValue);
                    break;
                case Ext.Date.SECOND:
                    testFrom = new Date(fromStat[0], fromStat[1], fromStat[2], fromStat[3],
                            fromStat[4], Math.floor(fromStat[5] / stepValue) * stepValue, 0);
                    break;
                case Ext.Date.MINUTE:
                    testFrom = new Date(fromStat[0], fromStat[1], fromStat[2], fromStat[3],
                            Math.floor(fromStat[4] / stepValue) * stepValue, 0, 0);
                    break;
                case Ext.Date.HOUR:
                    testFrom = new Date(fromStat[0], fromStat[1], fromStat[2],
                            Math.floor(fromStat[3] / stepValue) * stepValue, 0, 0, 0);
                    break;
                case Ext.Date.DAY:
                    testFrom = new Date(fromStat[0], fromStat[1],
                            Math.floor((fromStat[2] - 1) / stepValue) * stepValue + 1, 0, 0, 0, 0);
                    break;
                case Ext.Date.MONTH:
                    testFrom = new Date(fromStat[0], Math.floor(fromStat[1] / stepValue) * stepValue, 1, 0, 0, 0, 0);
                    steps = [];
                    stepsArray = true;
                    break;
                default: 
                    testFrom = new Date(Math.floor(fromStat[0] / stepValue) * stepValue, 0, 1, 0, 0, 0, 0);
                    steps = [];
                    stepsArray = true;
                    break;
            }
        }

        fractionalMonth = ((stepUnit === Ext.Date.MONTH) && (stepValue == 1/2 || stepValue == 1/3 || stepValue == 1/4));

        
        testTo = new Date(testFrom);
        while (testTo < to) {
            if (fractionalMonth) {
                date = new Date(testTo);
                year = date.getFullYear();
                month = date.getMonth();
                day = date.getDate();
                switch(stepValue) {
                    case 1/2:   
                        if (day >= 15) {
                            day = 1;
                            if (++month > 11) {
                                year++;
                            }
                        }
                        else {
                            day = 15;
                        }
                        break;

                    case 1/3:   
                        if (day >= 20) {
                            day = 1;
                            if (++month > 11) {
                                year++;
                            }
                        }
                        else {
                            if (day >= 10) {
                                day = 20
                            }
                            else {
                                day = 10;
                            }
                        }
                        break;

                    case 1/4:   
                        if (day >= 22) {
                            day = 1;
                            if (++month > 11) {
                                year++;
                            }
                        }
                        else {
                            if (day >= 15) {
                                day = 22
                            }
                            else {
                                if (day >= 8) {
                                    day = 15
                                }
                                else {
                                    day = 8;
                                }
                            }
                        }
                        break;
                }
                testTo.setYear(year);
                testTo.setMonth(month);
                testTo.setDate(day);
                steps.push(new Date(testTo));
            }
            else if (stepsArray) {
                testTo = Ext.Date.add(testTo, stepUnit, stepValue);
                steps.push(new Date(testTo));
            }
            else {
                testTo = Ext.Date.add(testTo, stepUnit, stepValue);                
                steps++;
            }
        }

        if (lockEnds) {
            testTo = to;
        }
        
        if (stepsArray) {
            return {
                from : +testFrom,
                to : +testTo,
                steps : steps   
            };            
        }
        else {
            return {
                from : +testFrom,
                to : +testTo,
                step : (testTo - testFrom) / steps,
                steps : steps   
            };            
        }
    },

    sorter: function (a, b) {
        return a.offset - b.offset;
    },

    rad: function(degrees) {
        return degrees % 360 * Math.PI / 180;
    },

    normalizeRadians: function(radian) {
        var twoPi = 2 * Math.PI;
        if (radian >= 0) {
            return radian % twoPi;
        }
        return ((radian % twoPi) + twoPi) % twoPi;
    },

    degrees: function(radian) {
        return radian * 180 / Math.PI % 360;
    },

    normalizeDegrees: function(degrees) {
        if (degrees >= 0) {
            return degrees % 360;
        }
        return ((degrees % 360) + 360) % 360;
    },

    withinBox: function(x, y, bbox) {
        bbox = bbox || {};
        return (x >= bbox.x && x <= (bbox.x + bbox.width) && y >= bbox.y && y <= (bbox.y + bbox.height));
    },

    parseGradient: function(gradient) {
        var me = this,
            type = gradient.type || 'linear',
            angle = gradient.angle || 0,
            radian = me.radian,
            stops = gradient.stops,
            stopsArr = [],
            stop,
            vector,
            max,
            stopObj;

        if (type == 'linear') {
            vector = [0, 0, Math.cos(angle * radian), Math.sin(angle * radian)];
            max = 1 / (Math.max(Math.abs(vector[2]), Math.abs(vector[3])) || 1);
            vector[2] *= max;
            vector[3] *= max;
            if (vector[2] < 0) {
                vector[0] = -vector[2];
                vector[2] = 0;
            }
            if (vector[3] < 0) {
                vector[1] = -vector[3];
                vector[3] = 0;
            }
        }

        for (stop in stops) {
            if (stops.hasOwnProperty(stop) && me.stopsRE.test(stop)) {
                stopObj = {
                    offset: parseInt(stop, 10),
                    color: Ext.draw.Color.toHex(stops[stop].color) || '#ffffff',
                    opacity: stops[stop].opacity || 1
                };
                stopsArr.push(stopObj);
            }
        }
        
        Ext.Array.sort(stopsArr, me.sorter);
        if (type == 'linear') {
            return {
                id: gradient.id,
                type: type,
                vector: vector,
                stops: stopsArr
            };
        }
        else {
            return {
                id: gradient.id,
                type: type,
                centerX: gradient.centerX,
                centerY: gradient.centerY,
                focalX: gradient.focalX,
                focalY: gradient.focalY,
                radius: gradient.radius,
                vector: vector,
                stops: stopsArr
            };
        }
    }
});


Ext.define('Ext.fx.PropertyHandler', {

    

                                

    statics: {
        defaultHandler: {
            pixelDefaultsRE: /width|height|top$|bottom$|left$|right$/i,
            unitRE: /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/,
            scrollRE: /^scroll/i,

            computeDelta: function(from, end, damper, initial, attr) {
                damper = (typeof damper == 'number') ? damper : 1;
                var unitRE = this.unitRE,
                    match = unitRE.exec(from),
                    start, units;
                if (match) {
                    from = match[1];
                    units = match[2];
                    if (!this.scrollRE.test(attr) && !units && this.pixelDefaultsRE.test(attr)) {
                        units = 'px';
                    }
                }
                from = +from || 0;

                match = unitRE.exec(end);
                if (match) {
                    end = match[1];
                    units = match[2] || units;
                }
                end = +end || 0;
                start = (initial != null) ? initial : from;
                return {
                    from: from,
                    delta: (end - start) * damper,
                    units: units
                };
            },

            get: function(from, end, damper, initialFrom, attr) {
                var ln = from.length,
                    out = [],
                    i, initial, res, j, len;
                for (i = 0; i < ln; i++) {
                    if (initialFrom) {
                        initial = initialFrom[i][1].from;
                    }
                    if (Ext.isArray(from[i][1]) && Ext.isArray(end)) {
                        res = [];
                        j = 0;
                        len = from[i][1].length;
                        for (; j < len; j++) {
                            res.push(this.computeDelta(from[i][1][j], end[j], damper, initial, attr));
                        }
                        out.push([from[i][0], res]);
                    }
                    else {
                        out.push([from[i][0], this.computeDelta(from[i][1], end, damper, initial, attr)]);
                    }
                }
                return out;
            },

            set: function(values, easing) {
                var ln = values.length,
                    out = [],
                    i, val, res, len, j;
                for (i = 0; i < ln; i++) {
                    val  = values[i][1];
                    if (Ext.isArray(val)) {
                        res = [];
                        j = 0;
                        len = val.length;
                        for (; j < len; j++) {
                            res.push(val[j].from + val[j].delta * easing + (val[j].units || 0));
                        }
                        out.push([values[i][0], res]);
                    } else {
                        out.push([values[i][0], val.from + val.delta * easing + (val.units || 0)]);
                    }
                }
                return out;
            }
        },
        stringHandler: {
            computeDelta: function(from, end, damper, initial, attr) {
                return {
                    from: from,
                    delta: end
                };
            },

            get: function(from, end, damper, initialFrom, attr) {
                var ln = from.length,
                    out = [],
                    i, initial, res, j, len;
                for (i = 0; i < ln; i++) {
                    out.push([from[i][0], this.computeDelta(from[i][1], end, damper, initial, attr)]);
                }
                return out;
            },

            set: function(values, easing) {
                var ln = values.length,
                    out = [],
                    i, val, res, len, j;
                for (i = 0; i < ln; i++) {
                    val  = values[i][1];
                    out.push([values[i][0], val.delta]);
                }
                return out;
            }
        },
        color: {
            rgbRE: /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,
            hexRE: /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,
            hex3RE: /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i,

            parseColor : function(color, damper) {
                damper = (typeof damper == 'number') ? damper : 1;
                var out    = false,
                    reList = [this.hexRE, this.rgbRE, this.hex3RE],
                    length = reList.length,
                    match, base, re, i;

                for (i = 0; i < length; i++) {
                    re = reList[i];

                    base = (i % 2 === 0) ? 16 : 10;
                    match = re.exec(color);
                    if (match && match.length === 4) {
                        if (i === 2) {
                            match[1] += match[1];
                            match[2] += match[2];
                            match[3] += match[3];
                        }
                        out = {
                            red: parseInt(match[1], base),
                            green: parseInt(match[2], base),
                            blue: parseInt(match[3], base)
                        };
                        break;
                    }
                }

                return out || color;
            },

            computeDelta: function(from, end, damper, initial) {
                from = this.parseColor(from);
                end = this.parseColor(end, damper);
                var start = initial ? initial : from,
                    tfrom = typeof start,
                    tend = typeof end;
                
                if (tfrom == 'string' ||  tfrom == 'undefined'
                  || tend == 'string' || tend == 'undefined') {
                    return end || start;
                }
                return {
                    from:  from,
                    delta: {
                        red: Math.round((end.red - start.red) * damper),
                        green: Math.round((end.green - start.green) * damper),
                        blue: Math.round((end.blue - start.blue) * damper)
                    }
                };
            },

            get: function(start, end, damper, initialFrom) {
                var ln = start.length,
                    out = [],
                    i, initial;
                for (i = 0; i < ln; i++) {
                    if (initialFrom) {
                        initial = initialFrom[i][1].from;
                    }
                    out.push([start[i][0], this.computeDelta(start[i][1], end, damper, initial)]);
                }
                return out;
            },

            set: function(values, easing) {
                var ln = values.length,
                    out = [],
                    i, val, parsedString, from, delta;
                for (i = 0; i < ln; i++) {
                    val = values[i][1];
                    if (val) {
                        from = val.from;
                        delta = val.delta;
                        
                        val = (typeof val == 'object' && 'red' in val)? 
                                'rgb(' + val.red + ', ' + val.green + ', ' + val.blue + ')' : val;
                        val = (typeof val == 'object' && val.length)? val[0] : val;
                        if (typeof val == 'undefined') {
                            return [];
                        }
                        parsedString = typeof val == 'string'? val :
                            'rgb(' + [
                                  (from.red + Math.round(delta.red * easing)) % 256,
                                  (from.green + Math.round(delta.green * easing)) % 256,
                                  (from.blue + Math.round(delta.blue * easing)) % 256
                              ].join(',') + ')';
                        out.push([
                            values[i][0],
                            parsedString
                        ]);
                    }
                }
                return out;
            }
        },
        object: {
            interpolate: function(prop, damper) {
                damper = (typeof damper == 'number') ? damper : 1;
                var out = {},
                    p;
                for(p in prop) {
                    out[p] = parseFloat(prop[p]) * damper;
                }
                return out;
            },

            computeDelta: function(from, end, damper, initial) {
                from = this.interpolate(from);
                end = this.interpolate(end, damper);
                var start = initial ? initial : from,
                    delta = {},
                    p;

                for(p in end) {
                    delta[p] = end[p] - start[p];
                }
                return {
                    from:  from,
                    delta: delta
                };
            },

            get: function(start, end, damper, initialFrom) {
                var ln = start.length,
                    out = [],
                    i, initial;
                for (i = 0; i < ln; i++) {
                    if (initialFrom) {
                        initial = initialFrom[i][1].from;
                    }
                    out.push([start[i][0], this.computeDelta(start[i][1], end, damper, initial)]);
                }
                return out;
            },

            set: function(values, easing) {
                var ln = values.length,
                    out = [],
                    outObject = {},
                    i, from, delta, val, p;
                for (i = 0; i < ln; i++) {
                    val  = values[i][1];
                    from = val.from;
                    delta = val.delta;
                    for (p in from) {
                        outObject[p] = from[p] + delta[p] * easing;
                    }
                    out.push([
                        values[i][0],
                        outObject
                    ]);
                }
                return out;
            }
        },

        path: {
            computeDelta: function(from, end, damper, initial) {
                damper = (typeof damper == 'number') ? damper : 1;
                var start;
                from = +from || 0;
                end = +end || 0;
                start = (initial != null) ? initial : from;
                return {
                    from: from,
                    delta: (end - start) * damper
                };
            },

            forcePath: function(path) {
                if (!Ext.isArray(path) && !Ext.isArray(path[0])) {
                    path = Ext.draw.Draw.parsePathString(path);
                }
                return path;
            },

            get: function(start, end, damper, initialFrom) {
                var endPath = this.forcePath(end),
                    out = [],
                    startLn = start.length,
                    startPathLn, pointsLn, i, deltaPath, initial, j, k, path, startPath;
                for (i = 0; i < startLn; i++) {
                    startPath = this.forcePath(start[i][1]);

                    deltaPath = Ext.draw.Draw.interpolatePaths(startPath, endPath);
                    startPath = deltaPath[0];
                    endPath = deltaPath[1];

                    startPathLn = startPath.length;
                    path = [];
                    for (j = 0; j < startPathLn; j++) {
                        deltaPath = [startPath[j][0]];
                        pointsLn = startPath[j].length;
                        for (k = 1; k < pointsLn; k++) {
                            initial = initialFrom && initialFrom[0][1][j][k].from;
                            deltaPath.push(this.computeDelta(startPath[j][k], endPath[j][k], damper, initial));
                        }
                        path.push(deltaPath);
                    }
                    out.push([start[i][0], path]);
                }
                return out;
            },

            set: function(values, easing) {
                var ln = values.length,
                    out = [],
                    i, j, k, newPath, calcPath, deltaPath, deltaPathLn, pointsLn;
                for (i = 0; i < ln; i++) {
                    deltaPath = values[i][1];
                    newPath = [];
                    deltaPathLn = deltaPath.length;
                    for (j = 0; j < deltaPathLn; j++) {
                        calcPath = [deltaPath[j][0]];
                        pointsLn = deltaPath[j].length;
                        for (k = 1; k < pointsLn; k++) {
                            calcPath.push(deltaPath[j][k].from + deltaPath[j][k].delta * easing);
                        }
                        newPath.push(calcPath.join(','));
                    }
                    out.push([values[i][0], newPath.join(',')]);
                }
                return out;
            }
        }
        
    }
}, function() {
    
    var props  = [
            'outlineColor',
            'backgroundColor',
            'borderColor',
            'borderTopColor',
            'borderRightColor',
            'borderBottomColor',
            'borderLeftColor',
            'fill',
            'stroke'
        ],
        length = props.length,
        i      = 0,
        prop;

    for (; i<length; i++) {
        prop = props[i];
        this[prop] = this.color;
    }
    
    
    props  = ['cursor'];
    length = props.length;
    i      = 0;

    for (; i<length; i++) {
        prop = props[i];
        this[prop] = this.stringHandler;
    }
});


Ext.define('Ext.fx.Anim', {

    

    mixins: {
        observable:  Ext.util.Observable 
    },

                                                                                                                     

    

    
    isAnimation: true,

    

    
    
    

    
    duration: 250,

    
    delay: 0,

    
    delayStart: 0,

    
    dynamic: false,

    
    easing: 'ease',

    

    
    damper: 1,

    
    bezierRE: /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,

    
    reverse: false,

    
    running: false,

    
    paused: false,

    
    iterations: 1,
    
    
    autoEnd: false,

    
    alternate: false,

    
    currentIteration: 0,

    
    startTime: 0,

    

    

    

    
    
    
    frameCount: 0,

    
    constructor: function(config) {
        var me = this,
            curve;
            
        config = config || {};
        
        if (config.keyframes) {
            return new Ext.fx.Animator(config);
        }
        Ext.apply(me, config);
        if (me.from === undefined) {
            me.from = {};
        }
        me.propHandlers = {};
        me.config = config;
        me.target = Ext.fx.Manager.createTarget(me.target);
        me.easingFn = Ext.fx.Easing[me.easing];
        me.target.dynamic = me.dynamic;

        
        if (!me.easingFn) {
            me.easingFn = String(me.easing).match(me.bezierRE);
            if (me.easingFn && me.easingFn.length == 5) {
                curve = me.easingFn;
                me.easingFn = Ext.fx.CubicBezier.cubicBezier(+curve[1], +curve[2], +curve[3], +curve[4]);
            }
        }
        me.id = Ext.id(null, 'ext-anim-');
        me.addEvents(
            
            'beforeanimate',
             
            'afteranimate',
             
            'lastframe'
        );
        me.mixins.observable.constructor.call(me);
        Ext.fx.Manager.addAnim(me);
        if (config.autoEnd) {
            me.running = true;
            me.jumpToEnd();
        }
    },

    
    setAttr: function(attr, value) {
        return Ext.fx.Manager.items.get(this.id).setAttr(this.target, attr, value);
    },

    
    initAttrs: function() {
        var me = this,
            from = me.from,
            to = me.to,
            initialFrom = me.initialFrom || {},
            out = {},
            start, end, propHandler, attr;

        for (attr in to) {
            if (to.hasOwnProperty(attr)) {
                start = me.target.getAttr(attr, from[attr]);
                end = to[attr];
                
                if (!Ext.fx.PropertyHandler[attr]) {
                    if (Ext.isObject(end)) {
                        propHandler = me.propHandlers[attr] = Ext.fx.PropertyHandler.object;
                    } else {
                        propHandler = me.propHandlers[attr] = Ext.fx.PropertyHandler.defaultHandler;
                    }
                }
                
                else {
                    propHandler = me.propHandlers[attr] = Ext.fx.PropertyHandler[attr];
                }
                out[attr] = propHandler.get(start, end, me.damper, initialFrom[attr], attr);
            }
        }
        me.currentAttrs = out;
    },

    
    start: function(startTime) {
        var me = this,
            delay = me.delay,
            delayStart = me.delayStart,
            delayDelta;
        
        if (delay) {
            if (!delayStart) {
                me.delayStart = startTime;
                return;
            }
            else {
                delayDelta = startTime - delayStart;
                if (delayDelta < delay) {
                    return;
                }
                else {
                    
                    startTime = new Date(delayStart.getTime() + delay);
                }
            }
        }
        if (me.fireEvent('beforeanimate', me) !== false) {
            me.startTime = startTime;
            if (!me.paused && !me.currentAttrs) {
                me.initAttrs();
            }
            me.running = true;
            me.frameCount = 0;
        }
    },
    
    
    jumpToEnd: function(){
        var me = this;
        
        if (!me.endWasCalled) {
            if (!me.currentAttrs) {
                me.initAttrs();
            }
            Ext.fx.Manager.jumpToEnd(me);
            me.end();
        }
    },

    
    runAnim: function(elapsedTime) {
        var me = this,
            attrs = me.currentAttrs,
            duration = me.duration,
            easingFn = me.easingFn,
            propHandlers = me.propHandlers,
            ret = {},
            easing, values, attr, lastFrame;

        if (elapsedTime >= duration) {
            elapsedTime = duration;
            lastFrame = true;
        }
        if (me.reverse) {
            elapsedTime = duration - elapsedTime;
        }

        for (attr in attrs) {
            if (attrs.hasOwnProperty(attr)) {
                values = attrs[attr];
                easing = lastFrame ? 1 : easingFn(elapsedTime / duration);
                ret[attr] = propHandlers[attr].set(values, easing);
            }
        }
        me.frameCount++;
            
        return ret;
    },

    
    lastFrame: function() {
        var me = this,
            iter = me.iterations,
            iterCount = me.currentIteration;

        iterCount++;
        if (iterCount < iter) {
            if (me.alternate) {
                me.reverse = !me.reverse;
            }
            me.startTime = new Date();
            me.currentIteration = iterCount;
            
            me.paused = false;
        }
        else {
            me.currentIteration = 0;
            me.end();
            me.fireEvent('lastframe', me, me.startTime);
        }
    },

    endWasCalled: 0,

    
    end: function() {
        var me = this;
        if (me.endWasCalled++) {
            return;
        }
        
        me.startTime = 0;
        me.paused = false;
        me.running = false;
        Ext.fx.Manager.removeAnim(me);
        me.fireEvent('afteranimate', me, me.startTime);
        Ext.callback(me.callback, me.scope, [me, me.startTime]);
        if (me.remove) {
            me.target.remove();
        }
    },
    
    isReady: function() {
        return this.paused === false && this.running === false && this.iterations > 0;
    },
    
    isRunning: function() {
        return this.paused === false && this.running === true && this.isAnimator !== true;
    }
});

Ext.enableFx = true;


Ext.define('Ext.util.Animate', {
               
                       
                                   
                          
                     
      
    
    isAnimate: true,

    
    animate: function(animObj) {
        var me = this;
        if (Ext.fx.Manager.hasFxBlock(me.id)) {
            return me;
        }
        Ext.fx.Manager.queueFx(new Ext.fx.Anim(me.anim(animObj)));
        return this;
    },

    
    anim: function(config) {
        if (!Ext.isObject(config)) {
            return (config) ? {} : false;
        }

        var me = this;

        if (config.stopAnimation) {
            me.stopAnimation();
        }

        Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id));

        return Ext.apply({
            target: me,
            paused: true
        }, config);
    },

    
    stopFx: Ext.Function.alias(Ext.util.Animate, 'stopAnimation'),

    
    stopAnimation: function() {
        Ext.fx.Manager.stopAnimation(this.id);
        return this;
    },

    
    syncFx: function() {
        Ext.fx.Manager.setFxDefaults(this.id, {
            concurrent: true
        });
        return this;
    },

    
    sequenceFx: function() {
        Ext.fx.Manager.setFxDefaults(this.id, {
            concurrent: false
        });
        return this;
    },

    
    hasActiveFx: Ext.Function.alias(Ext.util.Animate, 'getActiveAnimation'),

    
    getActiveAnimation: function() {
        return Ext.fx.Manager.getActiveAnimation(this.id);
    }
}, function(){
    
    Ext.applyIf(Ext.Element.prototype, this.prototype);
    
    Ext.CompositeElementLite.importElementMethods();
});


Ext.define('Ext.util.ElementContainer', {

    childEls: [
        
        
        
        
    ],

    constructor: function () {
        var me = this,
            childEls;

        
        
        if (me.hasOwnProperty('childEls')) {
            childEls = me.childEls;
            delete me.childEls;

            me.addChildEls.apply(me, childEls);
        }
    },

    destroy: function () {
        var me = this,
            childEls = me.getChildEls(),
            child, childName, i, k;

        for (i = childEls.length; i--; ) {
            childName = childEls[i];
            if (typeof childName != 'string') {
                childName = childName.name;
            }

            child = me[childName];
            if (child) {
                me[childName] = null; 
                child.remove();
            }
        }
    },

    
    addChildEls: function () {
        var me = this,
            args = arguments;

        if (me.hasOwnProperty('childEls')) {
            me.childEls.push.apply(me.childEls, args);
        } else {
            me.childEls = me.getChildEls().concat(Array.prototype.slice.call(args));
        }
        
        me.prune(me.childEls, false);
    },

    
    applyChildEls: function(el, id) {
        var me = this,
            childEls = me.getChildEls(),
            baseId, childName, i, selector, value;

        baseId = (id || me.id) + '-';
        for (i = childEls.length; i--; ) {
            childName = childEls[i];

            if (typeof childName == 'string') {
                
                
                value = el.getById(baseId + childName);
            } else {
                if ((selector = childName.select)) {
                    value = Ext.select(selector, true, el.dom); 
                } else if ((selector = childName.selectNode)) {
                    value = Ext.get(Ext.DomQuery.selectNode(selector, el.dom));
                } else {
                    
                    value = el.getById(childName.id || (baseId + childName.itemId));
                }

                childName = childName.name;
            }

            me[childName] = value;
        }
    },

    getChildEls: function () {
        var me = this,
            self;

        
        if (me.hasOwnProperty('childEls')) {
            return me.childEls;
        }

        
        
        self = me.self;
        return self.$childEls || me.getClassChildEls(self);
    },

    getClassChildEls: function (cls) {
        var me = this,
            result = cls.$childEls,
            childEls, i, length, forked, mixin, mixins, name, parts, proto, supr, superMixins;

        if (!result) {
            
            
            

            supr = cls.superclass;
            if (supr) {
                supr = supr.self;
                parts = [supr.$childEls || me.getClassChildEls(supr)]; 
                superMixins = supr.prototype.mixins || {};
            } else {
                parts = [];
                superMixins = {};
            }

            proto = cls.prototype;
            mixins = proto.mixins; 
            for (name in mixins) {
                if (mixins.hasOwnProperty(name) && !superMixins.hasOwnProperty(name)) {
                    mixin = mixins[name].self;
                    parts.push(mixin.$childEls || me.getClassChildEls(mixin));
                }
            }

            parts.push(proto.hasOwnProperty('childEls') && proto.childEls);

            for (i = 0, length = parts.length; i < length; ++i) {
                childEls = parts[i];
                if (childEls && childEls.length) {
                    if (!result) {
                        result = childEls;
                    } else {
                        if (!forked) {
                            forked = true;
                            result = result.slice(0);
                        }
                        result.push.apply(result, childEls);
                    }
                }
            }

            cls.$childEls = result = (result ? me.prune(result, !forked) : []);
        }

        return result;
    },

    prune: function (childEls, shared) {
        var index = childEls.length,
            map = {},
            name;

        while (index--) {
            name = childEls[index];
            if (typeof name != 'string') {
                name = name.name;
            }

            if (!map[name]) {
                map[name] = 1;
            } else {
                if (shared) {
                    shared = false;
                    childEls = childEls.slice(0);
                }
                Ext.Array.erase(childEls, index, 1);
            }
        }

        return childEls;
    },

    
    removeChildEls: function (testFn) {
        var me = this,
            old = me.getChildEls(),
            keepers = (me.childEls = []),
            n, i, cel;

        for (i = 0, n = old.length; i < n; ++i) {
            cel = old[i];
            if (!testFn(cel)) {
                keepers.push(cel);
            }
        }
    }
});


Ext.define('Ext.util.Renderable', {
               
                         
      

    frameCls: Ext.baseCSSPrefix + 'frame',

    frameIdRegex: /[\-]frame\d+[TMB][LCR]$/,
    
    frameElNames: ['TL','TC','TR','ML','MC','MR','BL','BC','BR','Table'],

    frameTpl: [
        '{%this.renderDockedItems(out,values,0);%}',
        '<tpl if="top">',
            '<tpl if="left"><div id="{fgid}TL" class="{frameCls}-tl {baseCls}-tl {baseCls}-{ui}-tl<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tl</tpl>{frameElCls}" role="presentation"></tpl>',
                '<tpl if="right"><div id="{fgid}TR" class="{frameCls}-tr {baseCls}-tr {baseCls}-{ui}-tr<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tr</tpl>{frameElCls}" role="presentation"></tpl>',
                    '<div id="{fgid}TC" class="{frameCls}-tc {baseCls}-tc {baseCls}-{ui}-tc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tc</tpl>{frameElCls}" role="presentation"></div>',
                '<tpl if="right"></div></tpl>',
            '<tpl if="left"></div></tpl>',
        '</tpl>',
        '<tpl if="left"><div id="{fgid}ML" class="{frameCls}-ml {baseCls}-ml {baseCls}-{ui}-ml<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-ml</tpl>{frameElCls}" role="presentation"></tpl>',
            '<tpl if="right"><div id="{fgid}MR" class="{frameCls}-mr {baseCls}-mr {baseCls}-{ui}-mr<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mr</tpl>{frameElCls}" role="presentation"></tpl>',
                '<div id="{fgid}MC" class="{frameCls}-mc {baseCls}-mc {baseCls}-{ui}-mc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mc</tpl>{frameElCls}" role="presentation">',
                    '{%this.applyRenderTpl(out, values)%}',
                '</div>',
            '<tpl if="right"></div></tpl>',
        '<tpl if="left"></div></tpl>',
        '<tpl if="bottom">',
            '<tpl if="left"><div id="{fgid}BL" class="{frameCls}-bl {baseCls}-bl {baseCls}-{ui}-bl<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bl</tpl>{frameElCls}" role="presentation"></tpl>',
                '<tpl if="right"><div id="{fgid}BR" class="{frameCls}-br {baseCls}-br {baseCls}-{ui}-br<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-br</tpl>{frameElCls}" role="presentation"></tpl>',
                    '<div id="{fgid}BC" class="{frameCls}-bc {baseCls}-bc {baseCls}-{ui}-bc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bc</tpl>{frameElCls}" role="presentation"></div>',
                '<tpl if="right"></div></tpl>',
            '<tpl if="left"></div></tpl>',
        '</tpl>',
        '{%this.renderDockedItems(out,values,1);%}'
    ],

    frameTableTpl: [
        '{%this.renderDockedItems(out,values,0);%}',
        '<table id="{fgid}Table" class="', Ext.plainTableCls, '" cellpadding="0" role="presentation">',
        '<tbody role="presentation">',
            '<tpl if="top">',
                '<tr role="presentation">',
                    '<tpl if="left"><td id="{fgid}TL" class="{frameCls}-tl {baseCls}-tl {baseCls}-{ui}-tl<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tl</tpl>{frameElCls}" role="presentation"></td></tpl>',
                    '<td id="{fgid}TC" class="{frameCls}-tc {baseCls}-tc {baseCls}-{ui}-tc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tc</tpl>{frameElCls}" role="presentation"></td>',
                    '<tpl if="right"><td id="{fgid}TR" class="{frameCls}-tr {baseCls}-tr {baseCls}-{ui}-tr<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tr</tpl>{frameElCls}" role="presentation"></td></tpl>',
                '</tr>',
            '</tpl>',
            '<tr role="presentation">',
                '<tpl if="left"><td id="{fgid}ML" class="{frameCls}-ml {baseCls}-ml {baseCls}-{ui}-ml<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-ml</tpl>{frameElCls}" role="presentation"></td></tpl>',
                '<td id="{fgid}MC" class="{frameCls}-mc {baseCls}-mc {baseCls}-{ui}-mc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mc</tpl>{frameElCls}" role="presentation">',
                    '{%this.applyRenderTpl(out, values)%}',
                '</td>',
                '<tpl if="right"><td id="{fgid}MR" class="{frameCls}-mr {baseCls}-mr {baseCls}-{ui}-mr<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mr</tpl>{frameElCls}" role="presentation"></td></tpl>',
            '</tr>',
            '<tpl if="bottom">',
                '<tr role="presentation">',
                    '<tpl if="left"><td id="{fgid}BL" class="{frameCls}-bl {baseCls}-bl {baseCls}-{ui}-bl<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bl</tpl>{frameElCls}" role="presentation"></td></tpl>',
                    '<td id="{fgid}BC" class="{frameCls}-bc {baseCls}-bc {baseCls}-{ui}-bc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bc</tpl>{frameElCls}" role="presentation"></td>',
                    '<tpl if="right"><td id="{fgid}BR" class="{frameCls}-br {baseCls}-br {baseCls}-{ui}-br<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-br</tpl>{frameElCls}" role="presentation"></td></tpl>',
                '</tr>',
            '</tpl>',
        '</tbody></table>',
        '{%this.renderDockedItems(out,values,1);%}'
    ],

    
    afterRender : function() {
        var me = this,
            data = {},
            protoEl = me.protoEl,
            target = me.el,
            item, pre, hide, contentEl;

        me.finishRenderChildren();
        
        
        
        if (me.contentEl) {
            pre = Ext.baseCSSPrefix;
            hide = pre + 'hide-';
            contentEl = Ext.get(me.contentEl);
            contentEl.removeCls([pre+'hidden', hide+'display', hide+'offsets', hide+'nosize']);
            me.getContentTarget().appendChild(contentEl.dom);
        }

        protoEl.writeTo(data);
        
        
        
        
        item = data.removed;
        if (item) {
            target.removeCls(item);
        }
        
        item = data.cls;
        if (item.length) {
            target.addCls(item);
        }
        
        item = data.style;
        if (data.style) {
            target.setStyle(item);
        }
        
        me.protoEl = null;

        
        if (!me.ownerCt) {
            me.updateLayout();
        }
    },

    afterFirstLayout : function(width, height) {
        var me = this,
            x = me.x,
            y = me.y,
            hasX,
            hasY,
            pos, xy;

        
        
        if (!me.ownerLayout) {
            hasX = Ext.isDefined(x);
            hasY = Ext.isDefined(y);
        }

        
        
        if (me.floating && (!hasX || !hasY)) {
            if (me.floatParent) {
                pos = me.floatParent.getTargetEl().getViewRegion();
                xy = me.el.getAlignToXY(me.floatParent.getTargetEl(), 'c-c');
                pos.x = xy[0] - pos.x;
                pos.y = xy[1] - pos.y;
            } else {
                xy = me.el.getAlignToXY(me.container, 'c-c');
                pos = me.container.translateXY(xy[0], xy[1]);
            }
            x = hasX ? x : pos.x;
            y = hasY ? y : pos.y;
            hasX = hasY = true;
        }

        if (hasX || hasY) {
            me.setPosition(x, y);
        }
        me.onBoxReady(width, height);
    },

    
    applyRenderSelectors: function() {
        var me = this,
            selectors = me.renderSelectors,
            el = me.el,
            dom = el.dom,
            selector;

        me.applyChildEls(el);

        
        
        
        if (selectors) {
            for (selector in selectors) {
                if (selectors.hasOwnProperty(selector) && selectors[selector]) {
                    me[selector] = Ext.get(Ext.DomQuery.selectNode(selectors[selector], dom));
                }
            }
        }
    },

    beforeRender: function () {
        var me = this,
            target = me.getTargetEl(),
            overflowEl = me.getOverflowEl(),
            layout = me.getComponentLayout(),
            
            overflowStyle = me.getOverflowStyle();

        
        me.frame = me.frame || me.alwaysFramed;

        if (!layout.initialized) {
            layout.initLayout();
        }

        
        
        if (overflowEl) {
            overflowEl.setStyle(overflowStyle);
            me.overflowStyleSet = true;
        }

        me.setUI(me.ui);

        if (me.disabled) {
            
            me.disable(true);
        }
    },

    
    doApplyRenderTpl: function(out, values) {
        
        

        var me = values.$comp,
            tpl;

        
        if (!me.rendered) {
            tpl = me.initRenderTpl();
            tpl.applyOut(values.renderData, out);
        }
    },

    
    doAutoRender: function() {
        var me = this;
        if (!me.rendered) {
            if (me.floating) {
                me.render(document.body);
            } else {
                me.render(Ext.isBoolean(me.autoRender) ? Ext.getBody() : me.autoRender);
            }
        }
    },

    doRenderContent: function (out, renderData) {
        
        

        var me = renderData.$comp;

        if (me.html) {
            Ext.DomHelper.generateMarkup(me.html, out);
            delete me.html;
        }

        if (me.tpl) {
            
            if (!me.tpl.isTemplate) {
                me.tpl = new Ext.XTemplate(me.tpl);
            }

            if (me.data) {
                
                me.tpl.applyOut(me.data, out);
                delete me.data;
            }
        }
    },

    doRenderFramingDockedItems: function (out, renderData, after) {
        
        

        var me = renderData.$comp;

        
        
        if (!me.rendered && me.doRenderDockedItems) {
            
            
            renderData.renderData.$skipDockedItems = true;

            
            
            me.doRenderDockedItems.call(this, out, renderData, after);
        }
    },

    
    finishRender: function(containerIdx) {
        var me = this,
            tpl, data, el;

        
        
        
        
        
        
        

        if (!me.el || me.$pid) {
            if (me.container) {
                el = me.container.getById(me.id, true);
            } else {
                el = Ext.getDom(me.id);
            }

            if (!me.el) {
                
                me.wrapPrimaryEl(el);
            } else {
                
                
                delete me.$pid;

                if (!me.el.dom) {
                    
                    me.wrapPrimaryEl(me.el);
                }
                el.parentNode.insertBefore(me.el.dom, el);
                Ext.removeNode(el); 
                
            }
        } else if (!me.rendering) {
            
            
            
            tpl = me.initRenderTpl();
            if (tpl) {
                data = me.initRenderData();
                tpl.insertFirst(me.getTargetEl(), data);
            }
        }
        

        if (!me.container) {
            
            me.container = Ext.get(me.el.dom.parentNode);
        }

        if (me.ctCls) {
            me.container.addCls(me.ctCls);
        }

        
        me.onRender(me.container, containerIdx);

        
        if (!me.overflowStyleSet) {
            me.getOverflowEl().setStyle(me.getOverflowStyle());
        }

        
        
        me.el.setVisibilityMode(Ext.Element[me.hideMode.toUpperCase()]);

        if (me.overCls) {
            me.el.hover(me.addOverCls, me.removeOverCls, me);
        }

        if (me.hasListeners.render) {
            me.fireEvent('render', me);
        }

        me.afterRender(); 
        if (me.hasListeners.afterrender) {
            me.fireEvent('afterrender', me);
        }
        me.initEvents();

        if (me.hidden) {
            
            
            
            me.el.hide();
        }
    },

    finishRenderChildren: function () {
        var layout = this.getComponentLayout();

        layout.finishRender();
    },

    getElConfig : function() {
        var me = this,
            autoEl = me.autoEl,
            frameInfo = me.getFrameInfo(),
            config = {
                tag: 'div',
                tpl: frameInfo ? me.initFramingTpl(frameInfo.table) : me.initRenderTpl()
            },
            protoEl = me.protoEl,
            i, frameElNames, len, suffix, frameGenId, frameData;

        me.initStyles(protoEl);
        protoEl.writeTo(config);
        protoEl.flush();

        if (Ext.isString(autoEl)) {
            config.tag = autoEl;
        } else {
            Ext.apply(config, autoEl); 
        }

        
        config.id = me.id;

        if (config.tpl) {
            
            if (frameInfo) {
                frameElNames = me.frameElNames;
                len = frameElNames.length;

                config.tplData = frameData = me.getFrameRenderData();
                frameData.renderData = me.initRenderData();
                frameGenId = frameData.fgid;

                
                for (i = 0; i < len; i++) {
                    suffix = frameElNames[i];
                    me.addChildEls({ name: 'frame' + suffix, id: frameGenId + suffix });
                }

                
                me.addChildEls({
                    name: 'frameBody',
                    id: frameGenId + 'MC'
                });
            } else {
                config.tplData = me.initRenderData();
            }
        }

        return config;
    },

    
    
    initFramingTpl: function(table) {
        var tpl = this.getFrameTpl(table);

        if (tpl && !tpl.applyRenderTpl) {
            this.setupFramingTpl(tpl);
        }

        return tpl;
    },

    
    setupFramingTpl: function(frameTpl) {
        frameTpl.applyRenderTpl = this.doApplyRenderTpl;
        frameTpl.renderDockedItems = this.doRenderFramingDockedItems;
    },

    
    getInsertPosition: function(position) {
        
        if (position !== undefined) {
            if (Ext.isNumber(position)) {
                position = this.container.dom.childNodes[position];
            }
            else {
                position = Ext.getDom(position);
            }
        }

        return position;
    },

    getRenderTree: function() {
        var me = this;

        if (!me.hasListeners.beforerender || me.fireEvent('beforerender', me) !== false) {
            me.beforeRender();

            
            
            me.rendering = true;

            if (me.el) {
                
                
                
                return {
                    tag: 'div',
                    role: 'presentation',
                    id: (me.$pid = Ext.id())
                };
            }

            return me.getElConfig();
        }

        return null;
    },

    initContainer: function(container) {
        var me = this;

        
        
        
        if (!container && me.el) {
            container = me.el.dom.parentNode;
            me.allowDomMove = false;
        }
        me.container = container.dom ? container : Ext.get(container);

        return me.container;
    },

    
    initRenderData: function() {
        var me = this;

        return Ext.apply({
            $comp: me,
            id: me.id,
            ui: me.ui,
            uiCls: me.uiCls,
            baseCls: me.baseCls,
            componentCls: me.componentCls,
            frame: me.frame,
            role: me.ariaRole,
            childElCls: '' 
        }, me.renderData);
    },

    
    initRenderTpl: function() {
        var tpl = this.getTpl('renderTpl');

        if (tpl && !tpl.renderContent) {
            this.setupRenderTpl(tpl);
        }

        return tpl;
    },

    
    onRender: function(parentNode, containerIdx) {
        var me = this,
            x = me.x,
            y = me.y,
            lastBox = null,
            width, height,
            el = me.el;

        me.applyRenderSelectors();

        
        
        me.rendering = null;

        me.rendered = true;

        
        if (x != null) {
            lastBox = {x:x};
        }
        if (y != null) {
            (lastBox = lastBox || {}).y = y;
        }
        
        
        
        if (!me.getFrameInfo() && Ext.isBorderBox) {
            width = me.width;
            height = me.height;

            if (typeof width === 'number') {
                lastBox = lastBox || {};
                lastBox.width = width;
            }
            if (typeof height === 'number') {
                lastBox = lastBox || {};
                lastBox.height = height;
            }
        }

        me.lastBox = el.lastBox = lastBox;
    },

    
    render: function(container, position) {
        var me = this,
            el = me.el && (me.el = Ext.get(me.el)), 
            vetoed,
            tree,
            nextSibling;

        Ext.suspendLayouts();

        container = me.initContainer(container);

        nextSibling = me.getInsertPosition(position);

        if (!el) {
            tree = me.getRenderTree();
            if (me.ownerLayout && me.ownerLayout.transformItemRenderTree) {
                tree = me.ownerLayout.transformItemRenderTree(tree);
            }

            
            if (tree) {
                if (nextSibling) {
                    el = Ext.DomHelper.insertBefore(nextSibling, tree);
                } else {
                    el = Ext.DomHelper.append(container, tree);
                }

                me.wrapPrimaryEl(el);
            }
        } else {
            if (!me.hasListeners.beforerender || me.fireEvent('beforerender', me) !== false) {
                me.beforeRender();
                
                me.initStyles(el);
                if (me.allowDomMove !== false) {
                    if (nextSibling) {
                        container.dom.insertBefore(el.dom, nextSibling);
                    } else {
                        container.dom.appendChild(el.dom);
                    }
                }
            } else {
                vetoed = true;
            }
        }

        if (el && !vetoed) {
            me.finishRender(position);
        }

        Ext.resumeLayouts(!me.hidden && !container.isDetachedBody);
    },

    
    ensureAttachedToBody: function (runLayout) {
        var comp = this,
            body;

        while (comp.ownerCt) {
            comp = comp.ownerCt;
        }

        if (comp.container.isDetachedBody) {
            comp.container = body = Ext.getBody();
            body.appendChild(comp.el.dom);
            if (runLayout) {
                comp.updateLayout();
            }
            if (typeof comp.x == 'number' || typeof comp.y == 'number') {
                comp.setPosition(comp.x, comp.y);
            }
        }
    },

    setupRenderTpl: function (renderTpl) {
        renderTpl.renderBody = renderTpl.renderContent = this.doRenderContent;
    },

    wrapPrimaryEl: function (dom) {
        this.el = Ext.get(dom, true);
    },

    
    initFrame : function() {
        if (Ext.supports.CSS3BorderRadius || !this.frame) {
            return;
        }

        var me = this,
            frameInfo = me.getFrameInfo(),
            frameTpl, frameGenId,
            frameElNames = me.frameElNames,
            len = frameElNames.length,
            i, frameData, suffix;

        if (frameInfo) {
            frameTpl = me.getFrameTpl(frameInfo.table);
            frameData = me.getFrameRenderData();
            frameGenId = frameData.fgid;

            
            
            frameTpl.insertFirst(me.el, frameData);

            
            
            me.frameBody = me.el.down('.' + me.frameCls + '-mc');

            
            me.removeChildEls(function (c) {
                return c.id && me.frameIdRegex.test(c.id);
            });

            
            for (i = 0; i < len; i++) {
                suffix = frameElNames[i];
                me['frame' + suffix] = me.el.getById(frameGenId + suffix);
            }
        }
    },

    getFrameRenderData: function () {
        var me = this,
            
            frameInfo = me.frameSize,
            frameGenId = (me.frameGenId || 0) + 1;

        
        
        me.frameGenId = frameGenId;

        return {
            $comp:      me,
            fgid:       me.id + '-frame' + frameGenId,
            ui:         me.ui,
            uiCls:      me.uiCls,
            frameCls:   me.frameCls,
            baseCls:    me.baseCls,
            top:        !!frameInfo.top,
            left:       !!frameInfo.left,
            right:      !!frameInfo.right,
            bottom:     !!frameInfo.bottom,
            
            
            frameElCls: ''
        };
    },

    updateFrame: function() {
        if (Ext.supports.CSS3BorderRadius || !this.frame) {
            return;
        }

        var me = this,
            wasTable = me.frameSize && me.frameSize.table,
            oldFrameTL = me.frameTL,
            oldFrameBL = me.frameBL,
            oldFrameML = me.frameML,
            oldFrameMC = me.frameMC,
            newMCClassName;

        me.initFrame();

        if (oldFrameMC) {
            if (me.frame) {

                
                newMCClassName = me.frameMC.dom.className;

                
                
                oldFrameMC.insertAfter(me.frameMC);
                me.frameMC.remove();

                
                me.frameBody = me.frameMC = oldFrameMC;

                
                oldFrameMC.dom.className = newMCClassName;

                
                if (wasTable) {
                    me.el.query('> table')[1].remove();
                }
                else {
                    if (oldFrameTL) {
                        oldFrameTL.remove();
                    }
                    if (oldFrameBL) {
                        oldFrameBL.remove();
                    }
                    if (oldFrameML) {
                        oldFrameML.remove();
                    }
                }
            }
        }
        else if (me.frame) {
            me.applyRenderSelectors();
        }
    },

    
    getFrameInfo: function() {
        
        if (Ext.supports.CSS3BorderRadius || !this.frame) {
            return false;
        }

        var me = this,
            frameInfoCache = me.frameInfoCache,
            cls = me.getFramingInfoCls() + '-frameInfo',
            frameInfo = frameInfoCache[cls],
            max = Math.max,
            styleEl, match, info, frameTop, frameRight, frameBottom, frameLeft,
            borderWidthT, borderWidthR, borderWidthB, borderWidthL,
            paddingT, paddingR, paddingB, paddingL,
            borderRadiusTL, borderRadiusTR, borderRadiusBR, borderRadiusBL;

        if (frameInfo == null) {
            
            styleEl = Ext.fly(me.getStyleProxy(cls), 'frame-style-el');
            info = styleEl.getStyle('font-family');

            if (info) {
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                info = info.split('-');
                
                borderRadiusTL = parseInt(info[1], 10);
                borderRadiusTR = parseInt(info[2], 10);
                borderRadiusBR = parseInt(info[3], 10);
                borderRadiusBL = parseInt(info[4], 10);
                borderWidthT   = parseInt(info[5], 10);
                borderWidthR   = parseInt(info[6], 10);
                borderWidthB   = parseInt(info[7], 10);
                borderWidthL   = parseInt(info[8], 10);
                paddingT       = parseInt(info[9], 10);
                paddingR       = parseInt(info[10], 10);
                paddingB       = parseInt(info[11], 10);
                paddingL       = parseInt(info[12], 10);

                
                
                frameTop    = max(borderWidthT, max(borderRadiusTL, borderRadiusTR));
                frameRight  = max(borderWidthR, max(borderRadiusTR, borderRadiusBR));
                frameBottom = max(borderWidthB, max(borderRadiusBL, borderRadiusBR));
                frameLeft   = max(borderWidthL, max(borderRadiusTL, borderRadiusBL));

                frameInfo = {
                    table: info[0].charAt(0) === 't',
                    vertical: info[0].charAt(1) === 'v',

                    top: frameTop,
                    right: frameRight,
                    bottom: frameBottom,
                    left: frameLeft,

                    width: frameLeft + frameRight,
                    height: frameTop + frameBottom,

                    maxWidth: max(frameTop, frameRight, frameBottom, frameLeft),

                    border: {
                        top:    borderWidthT,
                        right:  borderWidthR,
                        bottom: borderWidthB,
                        left:   borderWidthL,
                        width:  borderWidthL + borderWidthR,
                        height: borderWidthT + borderWidthB
                    },
                    padding: {
                        top:    paddingT,
                        right:  paddingR,
                        bottom: paddingB,
                        left:   paddingL,
                        width:  paddingL + paddingR,
                        height: paddingT + paddingB
                    },
                    radius: {
                        tl: borderRadiusTL,
                        tr: borderRadiusTR,
                        br: borderRadiusBR,
                        bl: borderRadiusBL
                    }
                };
            } else {
                frameInfo = false;
            }


            frameInfoCache[cls] = frameInfo;
        }

        me.frame = !!frameInfo;
        me.frameSize = frameInfo;

        return frameInfo;
    },
    
    getFramingInfoCls: function(){
        return this.baseCls + '-' + this.ui;
    },

    
    getStyleProxy: function(cls) {
        var result = this.styleProxyEl || (Ext.AbstractComponent.prototype.styleProxyEl = Ext.getBody().createChild({
                role: 'presentation',
                style: {
                    position: 'absolute',
                    top: '-10000px'
                }
            }, null, true));

        result.className = cls;
        return result;
    },

    
    getFrameTpl : function(table) {
        return this.getTpl(table ? 'frameTableTpl' : 'frameTpl');
    },

    
    frameInfoCache: {}
});


Ext.define('Ext.state.Provider', {
    mixins: {
        observable:  Ext.util.Observable 
    },
    
    
    prefix: 'ext-',
    
    constructor : function(config){
        config = config || {};
        var me = this;
        Ext.apply(me, config);
        
        me.addEvents("statechange");
        me.state = {};
        me.mixins.observable.constructor.call(me);
    },
    
    
    get : function(name, defaultValue){
        return typeof this.state[name] == "undefined" ?
            defaultValue : this.state[name];
    },

    
    clear : function(name){
        var me = this;
        delete me.state[name];
        me.fireEvent("statechange", me, name, null);
    },

    
    set : function(name, value){
        var me = this;
        me.state[name] = value;
        me.fireEvent("statechange", me, name, value);
    },

    
    decodeValue : function(value){

        
        
        
        
        
        
        

        var me = this,
            re = /^(a|n|d|b|s|o|e)\:(.*)$/,
            matches = re.exec(unescape(value)),
            all,
            type,
            keyValue,
            values,
            vLen,
            v;
            
        if(!matches || !matches[1]){
            return; 
        }
        
        type = matches[1];
        value = matches[2];
        switch (type) {
            case 'e':
                return null;
            case 'n':
                return parseFloat(value);
            case 'd':
                return new Date(Date.parse(value));
            case 'b':
                return (value == '1');
            case 'a':
                all = [];
                if(value != ''){
                    values = value.split('^');
                    vLen   = values.length;

                    for (v = 0; v < vLen; v++) {
                        value = values[v];
                        all.push(me.decodeValue(value));
                    }
                }
                return all;
           case 'o':
                all = {};
                if(value != ''){
                    values = value.split('^');
                    vLen   = values.length;

                    for (v = 0; v < vLen; v++) {
                        value = values[v];
                        keyValue         = value.split('=');
                        all[keyValue[0]] = me.decodeValue(keyValue[1]);
                    }
                }
                return all;
           default:
                return value;
        }
    },

    
    encodeValue : function(value){
        var flat = '',
            i = 0,
            enc,
            len,
            key;
            
        if (value == null) {
            return 'e:1';    
        } else if(typeof value == 'number') {
            enc = 'n:' + value;
        } else if(typeof value == 'boolean') {
            enc = 'b:' + (value ? '1' : '0');
        } else if(Ext.isDate(value)) {
            enc = 'd:' + value.toGMTString();
        } else if(Ext.isArray(value)) {
            for (len = value.length; i < len; i++) {
                flat += this.encodeValue(value[i]);
                if (i != len - 1) {
                    flat += '^';
                }
            }
            enc = 'a:' + flat;
        } else if (typeof value == 'object') {
            for (key in value) {
                if (typeof value[key] != 'function' && value[key] !== undefined) {
                    flat += key + '=' + this.encodeValue(value[key]) + '^';
                }
            }
            enc = 'o:' + flat.substring(0, flat.length-1);
        } else {
            enc = 's:' + value;
        }
        return escape(enc);
    }
});


Ext.define('Ext.state.Manager', {
    singleton: true,
                                     
    constructor: function() {
        this.provider = new Ext.state.Provider();
    },
    
    
    
    setProvider : function(stateProvider){
        this.provider = stateProvider;
    },

    
    get : function(key, defaultValue){
        return this.provider.get(key, defaultValue);
    },

    
     set : function(key, value){
        this.provider.set(key, value);
    },

    
    clear : function(key){
        this.provider.clear(key);
    },

    
    getProvider : function(){
        return this.provider;
    }
});


Ext.define('Ext.state.Stateful', {

    

    mixins: {
        observable:  Ext.util.Observable 
    },

                                    

    

    
    stateful: false,

    

    

    
    saveDelay: 100,

    constructor: function(config) {
        var me = this;

        config = config || {};
        if (config.stateful !== undefined) {
            me.stateful = config.stateful;
        }
        if (config.saveDelay !== undefined) {
            me.saveDelay = config.saveDelay;
        }
        me.stateId = me.stateId || config.stateId;

        if (!me.stateEvents) {
            me.stateEvents = [];
        }
        if (config.stateEvents) {
            me.stateEvents.concat(config.stateEvents);
        }
        this.addEvents(
            
            'beforestaterestore',

            
            'staterestore',

            
            'beforestatesave',

            
            'statesave'
        );
        me.mixins.observable.constructor.call(me);

        if (me.stateful !== false) {
            me.addStateEvents(me.stateEvents);
            me.initState();
        }
    },

    
    addStateEvents: function (events) {
        var me = this,
            i, event, stateEventsByName;

        if (me.stateful && me.getStateId()) {
            if (typeof events == 'string') {
                events = Array.prototype.slice.call(arguments, 0);
            }

            stateEventsByName = me.stateEventsByName || (me.stateEventsByName = {});

            for (i = events.length; i--; ) {
                event = events[i];

                if (!stateEventsByName[event]) {
                    stateEventsByName[event] = 1;
                    me.on(event, me.onStateChange, me);
                }
            }
        }
    },

    
    onStateChange: function(){
        var me = this,
            delay = me.saveDelay,
            statics, runner;

        if (!me.stateful) {
            return;
        }

        if (delay) {
            if (!me.stateTask) {
                statics = Ext.state.Stateful;
                runner = statics.runner || (statics.runner = new Ext.util.TaskRunner());

                me.stateTask = runner.newTask({
                    run: me.saveState,
                    scope: me,
                    interval: delay,
                    repeat: 1
                });
            }

            me.stateTask.start();
        } else {
            me.saveState();
        }
    },

    
    saveState: function() {
        var me = this,
            id = me.stateful && me.getStateId(),
            hasListeners = me.hasListeners,
            state;

        if (id) {
            state = me.getState() || {};    
            if (!hasListeners.beforestatesave || me.fireEvent('beforestatesave', me, state) !== false) {
                Ext.state.Manager.set(id, state);
                if (hasListeners.statesave) {
                    me.fireEvent('statesave', me, state);
                }
            }
        }
    },

    
    getState: function(){
        return null;
    },

    
    applyState: function(state) {
        if (state) {
            Ext.apply(this, state);
        }
    },

    
    getStateId: function() {
        var me = this;
        return me.stateId || (me.autoGenId ? null : me.id);
    },

    
    initState: function(){
        var me = this,
            id = me.stateful && me.getStateId(),
            hasListeners = me.hasListeners,
            state;

        if (id) {
            state = Ext.state.Manager.get(id);
            if (state) {
                state = Ext.apply({}, state);
                if (!hasListeners.beforestaterestore || me.fireEvent('beforestaterestore', me, state) !== false) {
                    me.applyState(state);
                    if (hasListeners.staterestore) {
                        me.fireEvent('staterestore', me, state);
                    }
                }
            }
        }
    },

    
    savePropToState: function (propName, state, stateName) {
        var me = this,
            value = me[propName],
            config = me.initialConfig;

        if (me.hasOwnProperty(propName)) {
            if (!config || config[propName] !== value) {
                if (state) {
                    state[stateName || propName] = value;
                }
                return true;
            }
        }
        return false;
    },

    
    savePropsToState: function (propNames, state) {
        var me = this,
            i, n;

        if (typeof propNames == 'string') {
            me.savePropToState(propNames, state);
        } else {
            for (i = 0, n = propNames.length; i < n; ++i) {
                me.savePropToState(propNames[i], state);
            }
        }

        return state;
    },

    
    destroy: function(){
        var me = this,
            task = me.stateTask;

        if (task) {
            task.destroy();
            me.stateTask = null;
        }

        me.clearListeners();
    }
});



Ext.define('Ext.AbstractComponent', {

    
               
                             
                               
                                
                                   
                           
      

    mixins: {
        positionable:  Ext.util.Positionable ,
        observable:  Ext.util.Observable ,
        animate:  Ext.util.Animate ,
        elementCt:  Ext.util.ElementContainer ,
        renderable:  Ext.util.Renderable ,
        state:  Ext.state.Stateful 
    },

    
    
           
                            
                      
                        
                        
                              
                           
                             
                            
                                    
                       
                           
      

    statics: {
        AUTO_ID: 1000,

        pendingLayouts: null,

        layoutSuspendCount: 0,

        
        cancelLayout: function(comp, isDestroying) {
            var context = this.runningLayoutContext || this.pendingLayouts;

            if (context) {
                context.cancelComponent(comp, false, isDestroying);
            }
        },

        
        flushLayouts: function () {
            var me = this,
                context = me.pendingLayouts;

            if (context && context.invalidQueue.length) {
                me.pendingLayouts = null;
                me.runningLayoutContext = context;

                Ext.override(context, {
                    runComplete: function () {
                        
                        
                        
                        me.runningLayoutContext = null;
                         
                        var result = this.callParent(); 
                        if (Ext.globalEvents.hasListeners.afterlayout) {                            
                            Ext.globalEvents.fireEvent('afterlayout');
                        }
                        return result;
                    }
                });

                context.run();
            }
        },

        
        resumeLayouts: function (flush) {
            if (this.layoutSuspendCount && ! --this.layoutSuspendCount) {
                if (flush) {
                    this.flushLayouts();
                }
                if (Ext.globalEvents.hasListeners.resumelayouts) {
                    Ext.globalEvents.fireEvent('resumelayouts');
                }
            }
        },

        
        suspendLayouts: function () {
            ++this.layoutSuspendCount;
        },

        
        updateLayout: function (comp, defer) {
            var me = this,
                running = me.runningLayoutContext,
                pending;

            if (running) {
                running.queueInvalidate(comp);
            } else {
                pending = me.pendingLayouts || (me.pendingLayouts = new Ext.layout.Context());
                pending.queueInvalidate(comp);

                if (!defer && !me.layoutSuspendCount && !comp.isLayoutSuspended()) {
                    me.flushLayouts();
                }
            }
        }
    },

    

    
    isComponent: true,

    
    getAutoId: function() {
        this.autoGenId = true;
        return ++Ext.AbstractComponent.AUTO_ID;
    },

    deferLayouts: false,

    

     
    autoGenId: false,

    

    

    

    
    renderTpl: '{%this.renderContent(out,values)%}',

    

    

    

    

    

    
    frameSize: null,

    

    

    

    

    
    tplWriteMode: 'overwrite',

    
    baseCls: Ext.baseCSSPrefix + 'component',

    

    

    

    
    disabledCls: Ext.baseCSSPrefix + 'item-disabled',

    
    ui: 'default',

    
    uiCls: [],

    

    

    

    

    

    

    
    hidden: false,

    
    disabled: false,

    

    
    draggable: false,

    
    floating: false,

    
    hideMode: 'display',

    

    

    
    
    
    

    

    

    
    autoShow: false,

    
    autoRender: false,

    
    allowDomMove: true,

    

    
    rendered: false,

    
    componentLayoutCounter: 0,

    
    shrinkWrap: 2,

    
    weight: 0,

    
    maskOnDisable: true,

    
    _isLayoutRoot: false,

     
    contentPaddingProperty: 'padding',
    
    horizontalPosProp: 'left',

    
    borderBoxCls: Ext.baseCSSPrefix + 'border-box',
    rootCls: Ext.baseCSSPrefix + 'body',

    
    constructor : function(config) {
        var me = this,
            i, len, xhooks;

        if (config) {
            Ext.apply(me, config);

            xhooks = me.xhooks;
            if (xhooks) {
                delete me.xhooks;
                Ext.override(me, xhooks);
            }
        } else {
            config = {};
        }

        me.initialConfig = config;

        me.mixins.elementCt.constructor.call(me);

        me.addEvents(
            
            'beforeactivate',
            
            'activate',
            
            'beforedeactivate',
            
            'deactivate',
            
            'added',
            
            'disable',
            
            'enable',
            
            'beforeshow',
            
            'show',
            
            'beforehide',
            
            'hide',
            
            'removed',
            
            'beforerender',
            
            'render',
            
            'afterrender',
            
            'boxready',
            
            'beforedestroy',
            
            'destroy',
            
            'resize',
            
             'move',
            
            'focus',
            
            'blur'
        );

        me.getId();

        me.setupProtoEl();

        
        
        if (me.cls) {
            me.initialCls = me.cls;
            me.protoEl.addCls(me.cls);
        }
        if (me.style) {
            me.initialStyle = me.style;
            me.protoEl.setStyle(me.style);
        }

        me.renderData = me.renderData || {};
        me.renderSelectors = me.renderSelectors || {};

        if (me.plugins) {
            me.plugins = me.constructPlugins();
        }

        
        if (!me.hasListeners) {
            me.hasListeners = new me.HasListeners();
        }

        me.initComponent();

        
        Ext.ComponentManager.register(me);

        
        me.mixins.observable.constructor.call(me);
        me.mixins.state.constructor.call(me, config);

        
        this.addStateEvents('resize');

        
        if (me.plugins) {
            for (i = 0, len = me.plugins.length; i < len; i++) {
                me.plugins[i] = me.initPlugin(me.plugins[i]);
            }
        }

        me.loader = me.getLoader();

        if (me.renderTo) {
            me.render(me.renderTo);
            
            
            
        }

        
        
        if (me.autoShow && !me.isContained) {
            me.show();
        }

    },

    initComponent: function () {
        var me = this;

        
        
        if (me.plugins && !me.plugins.processed) {
            me.plugins = me.constructPlugins();
        }

        
        
        me.setSize(me.width, me.height);
    },

    
    getState: function() {
        var me = this,
            state = null,
            sizeModel = me.getSizeModel();

        if (sizeModel.width.configured) {
            state = me.addPropertyToState(state, 'width');
        }
        if (sizeModel.height.configured) {
            state = me.addPropertyToState(state, 'height');
        }

        return state;
    },

    
    addPropertyToState: function (state, propName, value) {
        var me = this,
            len = arguments.length;

        
        
        if (len == 3 || me.hasOwnProperty(propName)) {
            if (len < 3) {
                value = me[propName];
            }

            
            
            if (value !== me.initialConfig[propName]) {
                (state || (state = {}))[propName] = value;
            }
        }

        return state;
    },

    show: Ext.emptyFn,

    animate: function(animObj) {
        var me = this,
            hasToWidth,
            hasToHeight,
            toHeight,
            toWidth,
            to,
            clearWidth,
            clearHeight,
            curWidth, w, curHeight, h, isExpanding,
            wasConstrained,
            wasConstrainedHeader,
            passedCallback,
            oldOverflow;

        animObj = animObj || {};
        to = animObj.to || {};

        if (Ext.fx.Manager.hasFxBlock(me.id)) {
            return me;
        }

        hasToWidth = Ext.isDefined(to.width);
        if (hasToWidth) {
            toWidth = Ext.Number.constrain(to.width, me.minWidth, me.maxWidth);
        }

        hasToHeight = Ext.isDefined(to.height);
        if (hasToHeight) {
            toHeight = Ext.Number.constrain(to.height, me.minHeight, me.maxHeight);
        }

        
        if (!animObj.dynamic && (hasToWidth || hasToHeight)) {
            curWidth = (animObj.from ? animObj.from.width : undefined) || me.getWidth();
            w = curWidth;
            curHeight = (animObj.from ? animObj.from.height : undefined) || me.getHeight();
            h = curHeight;
            isExpanding = false;

            if (hasToHeight && toHeight > curHeight) {
                h = toHeight;
                isExpanding = true;
            }
            if (hasToWidth && toWidth > curWidth) {
                w = toWidth;
                isExpanding = true;
            }

            
            if (hasToHeight || hasToWidth) {
                oldOverflow = me.el.getStyle('overtflow');
                if (oldOverflow !== 'hidden') {
                    me.el.setStyle('overflow', 'hidden');
                }
            }

            
            
            
            if (isExpanding) {
                clearWidth = !Ext.isNumber(me.width);
                clearHeight = !Ext.isNumber(me.height);

                
                
                
                me.setSize(w, h);
                me.el.setSize(curWidth, curHeight);

                if (clearWidth) {
                    delete me.width;
                }
                if (clearHeight) {
                    delete me.height;
                }
            }
            if (hasToWidth) {
                to.width = toWidth;
            }

            if (hasToHeight) {
                to.height = toHeight;
            }
        }

        
        
        wasConstrained = me.constrain;
        wasConstrainedHeader = me.constrainHeader;
        if (wasConstrained || wasConstrainedHeader) {
            me.constrain = me.constrainHeader = false;
            passedCallback = animObj.callback;
            animObj.callback = function() {
                me.constrain = wasConstrained;
                me.constrainHeader = wasConstrainedHeader;
                
                if (passedCallback) {
                    passedCallback.call(animObj.scope||me, arguments);
                }
                if (oldOverflow !== 'hidden') {
                    me.el.setStyle('overflow', oldOverflow);
                }
            };
        }
        return me.mixins.animate.animate.apply(me, arguments);
    },
    
    setHiddenState: function(hidden){
        var hierarchyState = this.getHierarchyState();
        
        this.hidden = hidden;
        if (hidden) {
            hierarchyState.hidden = true;
        } else {
            delete hierarchyState.hidden;
        }
    },

    onHide: function() {
        
        if (this.ownerLayout) {
            this.updateLayout({ isRoot: false });
        }
    },

    onShow : function() {
        this.updateLayout({ isRoot: false });
    },

    
    constructPlugin: function(plugin) {
        var me = this;
        
        
        if (typeof plugin == 'string') {
            plugin = Ext.PluginManager.create({}, plugin, me);
        }
        
        else {
            plugin = Ext.PluginManager.create(plugin, null, me);
        }
        return plugin;
    },

    
    constructPlugins: function() {
        var me = this,
            plugins = me.plugins,
            result, i, len;

        if (plugins) {
            result = [];

            
            
            
            result.processed = true;
            if (!Ext.isArray(plugins)) {
                plugins = [ plugins ];
            }
            for (i = 0, len = plugins.length; i < len; i++) {
                
                result[i] = me.constructPlugin(plugins[i]);
            }
        }

        me.pluginsInitialized = true;
        return result;
    },

    
    initPlugin : function(plugin) {
        plugin.init(this);

        return plugin;
    },

    
    
    addPlugin: function(plugin) {
        var me = this;

        plugin = me.constructPlugin(plugin);
        if (me.plugins) {
            me.plugins.push(plugin);
        } else {
            me.plugins = [ plugin ];
        }
        if (me.pluginsInitialized) {
            me.initPlugin(plugin);
        }
        return plugin;
    },

    removePlugin: function(plugin) {
        Ext.Array.remove(this.plugins, plugin);
        plugin.destroy();
    },

    
    findPlugin: function(ptype) {
        var i,
            plugins = this.plugins,
            ln = plugins && plugins.length;
        for (i = 0; i < ln; i++) {
            if (plugins[i].ptype === ptype) {
                return plugins[i];
            }
        }
    },

    
    getPlugin: function(pluginId) {
        var i,
            plugins = this.plugins,
            ln = plugins && plugins.length;
        for (i = 0; i < ln; i++) {
            if (plugins[i].pluginId === pluginId) {
                return plugins[i];
            }
        }
    },

    
    beforeLayout: Ext.emptyFn,

    
    registerFloatingItem: function(cmp) {
        var me = this;
        if (!me.floatingDescendants) {
            me.floatingDescendants = new Ext.ZIndexManager(me);
        }
        me.floatingDescendants.register(cmp);
    },

    unregisterFloatingItem: function(cmp) {
        var me = this;
        if (me.floatingDescendants) {
            me.floatingDescendants.unregister(cmp);
        }
    },

    layoutSuspendCount: 0,

    suspendLayouts: function () {
        var me = this;
        if (!me.rendered) {
            return;
        }
        if (++me.layoutSuspendCount == 1) {
            me.suspendLayout = true;
        }
    },

    resumeLayouts: function (flushOptions) {
        var me = this;
        if (!me.rendered) {
            return;
        }
        if (! --me.layoutSuspendCount) {
            me.suspendLayout = false;
            if (flushOptions && !me.isLayoutSuspended()) {
                me.updateLayout(flushOptions);
            }
        }
    },

    setupProtoEl: function() {
        var cls = this.initCls();

        this.protoEl = new Ext.util.ProtoElement({
            cls: cls.join(' ') 
        });
    },

    initCls: function() {
        var me = this,
            cls = [ me.baseCls, me.getComponentLayout().targetCls ];

        if (Ext.isDefined(me.cmpCls)) {
            if (Ext.isDefined(Ext.global.console)) {
                Ext.global.console.warn('Ext.Component: cmpCls has been deprecated. Please use componentCls.');
            }
            me.componentCls = me.cmpCls;
            delete me.cmpCls;
        }

        if (me.componentCls) {
            cls.push(me.componentCls);
        } else {
            me.componentCls = me.baseCls;
        }

        return cls;
    },

    
    setUI: function(ui) {
        var me = this,
            uiCls = me.uiCls,
            activeUI = me.activeUI,
            classes;

        if (ui === activeUI) {
            
            return;
        }

        
        if (activeUI) {
            classes = me.removeClsWithUI(uiCls, true);

            if (classes.length) {
                me.removeCls(classes);
            }

            
            me.removeUIFromElement();
        }
        else {
            
            me.uiCls = [];
        }

        
        me.ui = ui;

        
        
        me.activeUI = ui;

        
        me.addUIToElement();

        classes = me.addClsWithUI(uiCls, true);

        if (classes.length) {
            me.addCls(classes);
        }

        
        
        
        if (me.rendered) {
            me.updateLayout();
        }
    },

    
    addClsWithUI: function(classes, skip) {
        var me = this,
            clsArray = [],
            i = 0,
            uiCls = me.uiCls = Ext.Array.clone(me.uiCls),
            activeUI = me.activeUI,
            length,
            cls;

        if (typeof classes === "string") {
            classes = (classes.indexOf(' ') < 0) ? [classes] : Ext.String.splitWords(classes);
        }

        length = classes.length;

        for (; i < length; i++) {
            cls = classes[i];

            if (cls && !me.hasUICls(cls)) {
                uiCls.push(cls);

                
                if (activeUI) {
                    clsArray = clsArray.concat(me.addUIClsToElement(cls));
                }
            }
        }

        if (skip !== true && activeUI) {
            me.addCls(clsArray);
        }

        return clsArray;
    },

    
    removeClsWithUI: function(classes, skip) {
        var me = this,
            clsArray = [],
            i = 0,
            extArray = Ext.Array,
            remove = extArray.remove,
            uiCls = me.uiCls = extArray.clone(me.uiCls),
            activeUI = me.activeUI,
            length, cls;

        if (typeof classes === "string") {
            classes = (classes.indexOf(' ') < 0) ? [classes] : Ext.String.splitWords(classes);
        }

        length = classes.length;

        for (i = 0; i < length; i++) {
            cls = classes[i];

            if (cls && me.hasUICls(cls)) {
                remove(uiCls, cls);

                
                if (activeUI) {
                    clsArray = clsArray.concat(me.removeUIClsFromElement(cls));
                }
            }
        }

        if (skip !== true && activeUI) {
            me.removeCls(clsArray);
        }

        return clsArray;
    },

    
    hasUICls: function(cls) {
        var me = this,
            uiCls = me.uiCls || [];

        return Ext.Array.contains(uiCls, cls);
    },

    frameElementsArray: ['tl', 'tc', 'tr', 'ml', 'mc', 'mr', 'bl', 'bc', 'br'],

    
    addUIClsToElement: function(cls) {
        var me = this,
            baseClsUi = me.baseCls + '-' + me.ui + '-' + cls,
            result = [Ext.baseCSSPrefix + cls, me.baseCls + '-' + cls, baseClsUi],
            frameElementsArray, frameElementsLength, i, el, frameElement;

        if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) {
            
            frameElementsArray = me.frameElementsArray;
            frameElementsLength = frameElementsArray.length;

            
            for (i = 0; i < frameElementsLength; i++) {
                frameElement = frameElementsArray[i];
                el = me['frame' + frameElement.toUpperCase()];

                if (el) {
                    el.addCls(baseClsUi + '-' + frameElement);
                }
            }
        }

        return result;
    },

    
    removeUIClsFromElement: function(cls) {
        var me = this,
            baseClsUi = me.baseCls + '-' + me.ui + '-' + cls,
            result = [Ext.baseCSSPrefix + cls, me.baseCls + '-' + cls, baseClsUi],
            frameElementsArray, frameElementsLength, i, el, frameElement;

        if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) {
            
            frameElementsArray = me.frameElementsArray;
            frameElementsLength = frameElementsArray.length;

            
            for (i = 0; i < frameElementsLength; i++) {
                frameElement = frameElementsArray[i];
                el = me['frame' + frameElement.toUpperCase()];

                if (el) {
                    el.removeCls(baseClsUi + '-' + frameElement);
                }
            }
        }

        return result;
    },

    
    addUIToElement: function() {
        var me = this,
            baseClsUI = me.baseCls + '-' + me.ui,
            frameElementsArray, frameElementsLength, i, el, frameElement;

        me.addCls(baseClsUI);

        if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) {
            
            frameElementsArray = me.frameElementsArray;
            frameElementsLength = frameElementsArray.length;

            
            for (i = 0; i < frameElementsLength; i++) {
                frameElement = frameElementsArray[i];
                el = me['frame' + frameElement.toUpperCase()];

                if (el) {
                    el.addCls(baseClsUI + '-' + frameElement);
                }
            }
        }
    },

    
    removeUIFromElement: function() {
        var me = this,
            baseClsUI = me.baseCls + '-' + me.ui,
            frameElementsArray, frameElementsLength, i, el, frameElement;

        me.removeCls(baseClsUI);

        if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) {
            
            frameElementsArray = me.frameElementsArray;
            frameElementsLength = frameElementsArray.length;

            for (i = 0; i < frameElementsLength; i++) {
                frameElement = frameElementsArray[i];
                el = me['frame' + frameElement.toUpperCase()];

                if (el) {
                    el.removeCls(baseClsUI + '-' + frameElement);
                }
            }
        }
    },

    
    getTpl: function(name) {
        return Ext.XTemplate.getTpl(this, name);
    },

    
    initStyles: function(targetEl) {
        var me = this,
            Element = Ext.Element,
            margin = me.margin,
            border = me.border,
            cls = me.cls,
            style = me.style,
            x = me.x,
            y = me.y,
            width, height;

        me.initPadding(targetEl);

        if (margin != null) {
            targetEl.setStyle('margin', this.unitizeBox((margin === true) ? 5 : margin));
        }

        if (border != null) {
            me.setBorder(border, targetEl);
        }

        
        
        if (cls && cls != me.initialCls) {
            targetEl.addCls(cls);
            me.cls = me.initialCls = null;
        }
        if (style && style != me.initialStyle) {
            targetEl.setStyle(style);
            me.style = me.initialStyle = null;
        }

        if (x != null) {
            targetEl.setStyle(me.horizontalPosProp, (typeof x == 'number') ? (x + 'px') : x);
        }
        if (y != null) {
            targetEl.setStyle('top', (typeof y == 'number') ? (y + 'px') : y);
        }

        if (!me.ownerCt || me.floating) {
            if (Ext.scopeCss) {
                targetEl.addCls(me.rootCls);
            }
            if (Ext.isBorderBox) {
                targetEl.addCls(me.borderBoxCls);
            }
        }

        
        
        if (!me.getFrameInfo()) {
            width = me.width;
            height = me.height;

            
            if (width != null) {
                if (typeof width === 'number') {
                    if (Ext.isBorderBox) {
                        targetEl.setStyle('width', width + 'px');
                    }
                } else {
                    targetEl.setStyle('width', width);
                }
            }
            if (height != null) {
                if (typeof height === 'number') {
                    if (Ext.isBorderBox) {
                        targetEl.setStyle('height', height + 'px');
                    }
                } else {
                    targetEl.setStyle('height', height);
                }
            }
        }
    },

    
    initPadding: function(targetEl) {
        var me = this,
            padding = me.padding;

        if (padding != null) {
            if (me.layout && me.layout.managePadding && me.contentPaddingProperty === 'padding') {
                
                
                
                
                
                targetEl.setStyle('padding', 0);
            } else {
                
                
                targetEl.setStyle('padding', this.unitizeBox((padding === true) ? 5 : padding));
            }
        }
    },
    
    parseBox: function(box) {
        return Ext.dom.Element.parseBox(box);    
    },
    
    unitizeBox: function(box) {
        return Ext.dom.Element.unitizeBox(box);    
    },
    
    
    setMargin: function(margin,  preventLayout) {
        var me = this;
        
        if (me.rendered) {
            if (!margin && margin !== 0) {
                margin = '';
            } else {
                if (margin === true) {
                    margin = 5;
                }
                margin = this.unitizeBox(margin);
            }
            me.getTargetEl().setStyle('margin', margin);
            if (!preventLayout) {
                me.updateLayout();
            }
        } else {
            me.margin = margin;
        }
    },

    
    initEvents : function() {
        var me = this,
            afterRenderEvents = me.afterRenderEvents,
            afterRenderEvent, el, property, index, len;

        if (afterRenderEvents) {
            for (property in afterRenderEvents) {
                el = me[property];

                if (el && el.on) {
                    afterRenderEvent = afterRenderEvents[property];

                    for (index = 0, len = afterRenderEvent.length ; index < len ; ++index) {
                        me.mon(el, afterRenderEvent[index]);
                     }
                 }
            }
        }

        
        
        
        me.addFocusListener();
    },

    
    addFocusListener: function() {
        var me = this,
            focusEl = me.getFocusEl(),
            needsTabIndex;

        
        
        
        
        
        

        
        if (focusEl) {
            
            
            if (focusEl.isComponent) {
                return focusEl.addFocusListener();
            }

            
            
            
            needsTabIndex = focusEl.needsTabIndex();
            if (!me.focusListenerAdded && (!needsTabIndex || Ext.FocusManager.enabled)) {
                if (needsTabIndex) {
                    focusEl.dom.tabIndex = -1;
                }
                focusEl.on({
                    focus: me.onFocus,
                    blur: me.onBlur,
                    scope: me
                });
                me.focusListenerAdded = true;
            }
        }
    },

    
    getFocusEl: Ext.emptyFn,

    isFocusable: function() {
        var me = this,
            focusEl;
        if ((me.focusable !== false) && (focusEl = me.getFocusEl()) && me.rendered && !me.destroying && !me.isDestroyed && !me.disabled && me.isVisible(true)) {

            
            
            
            return focusEl.isFocusable(true);
        }
    },

    
    beforeFocus: Ext.emptyFn,

    
    onFocus: function(e) {
        var me = this,
            focusCls = me.focusCls,
            focusEl = me.getFocusEl();

        if (!me.disabled) {
            me.beforeFocus(e);
            if (focusCls && focusEl) {
                focusEl.addCls(me.addClsWithUI(focusCls, true));
            }
            if (!me.hasFocus) {
                me.hasFocus = true;
                me.fireEvent('focus', me, e);
            }
        }
    },

    
    beforeBlur : Ext.emptyFn,

    
    onBlur : function(e) {
        var me = this,
            focusCls = me.focusCls,
            focusEl = me.getFocusEl();

        if (me.destroying) {
            return;
        }

        me.beforeBlur(e);
        if (focusCls && focusEl) {
            focusEl.removeCls(me.removeClsWithUI(focusCls, true));
        }
        if (me.validateOnBlur) {
            me.validate();
        }
        me.hasFocus = false;
        me.fireEvent('blur', me, e);
        me.postBlur(e);
    },

    
    postBlur : Ext.emptyFn,

    
    is: function(selector) {
        return Ext.ComponentQuery.is(this, selector);
    },

    
    up: function (selector, limit) {
        var result = this.getRefOwner(),
            limitSelector = typeof limit === 'string',
            limitCount = typeof limit === 'number',
            limitComponent = limit && limit.isComponent,
            steps = 0;

        if (selector) {
            for (; result; result = result.getRefOwner()) {
                steps++;
                if (selector.isComponent) {
                    if (result === selector) {
                        return result;
                    }
                } else {
                    if (Ext.ComponentQuery.is(result, selector)) {
                        return result;
                    }
                }

                
                if (limitSelector && result.is(limit)) {
                    return;
                }
                if (limitCount && steps === limit) {
                    return;
                }
                if (limitComponent && result === limit) {
                    return;
                }
            }
        }
        return result;
    },

    
    nextSibling: function(selector) {
        var o = this.ownerCt, it, last, idx, c;
        if (o) {
            it = o.items;
            idx = it.indexOf(this) + 1;
            if (idx) {
                if (selector) {
                    for (last = it.getCount(); idx < last; idx++) {
                        if ((c = it.getAt(idx)).is(selector)) {
                            return c;
                        }
                    }
                } else {
                    if (idx < it.getCount()) {
                        return it.getAt(idx);
                    }
                }
            }
        }
        return null;
    },

    
    previousSibling: function(selector) {
        var o = this.ownerCt, it, idx, c;
        if (o) {
            it = o.items;
            idx = it.indexOf(this);
            if (idx != -1) {
                if (selector) {
                    for (--idx; idx >= 0; idx--) {
                        if ((c = it.getAt(idx)).is(selector)) {
                            return c;
                        }
                    }
                } else {
                    if (idx) {
                        return it.getAt(--idx);
                    }
                }
            }
        }
        return null;
    },

    
    previousNode: function(selector,  includeSelf) {
        var node = this,
            ownerCt = node.ownerCt,
            result,
            it, i, sib;

        
        if (includeSelf && node.is(selector)) {
            return node;
        }

        if (ownerCt) {
            for (it = ownerCt.items.items, i = Ext.Array.indexOf(it, node) - 1; i > -1; i--) {
                sib = it[i];
                if (sib.query) {
                    result = sib.query(selector);
                    result = result[result.length - 1];
                    if (result) {
                        return result;
                    }
                }
                if (sib.is(selector)) {
                    return sib;
                }
            }
            return ownerCt.previousNode(selector, true);
        }
        return null;
    },

    
    nextNode: function(selector,  includeSelf) {
        var node = this,
            ownerCt = node.ownerCt,
            result,
            it, len, i, sib;

        
        if (includeSelf && node.is(selector)) {
            return node;
        }

        if (ownerCt) {
            for (it = ownerCt.items.items, i = Ext.Array.indexOf(it, node) + 1, len = it.length; i < len; i++) {
                sib = it[i];
                if (sib.is(selector)) {
                    return sib;
                }
                if (sib.down) {
                    result = sib.down(selector);
                    if (result) {
                        return result;
                    }
                }
            }
            return ownerCt.nextNode(selector);
        }
        return null;
    },

    
    getId : function() {
        return this.id || (this.id = 'ext-comp-' + (this.getAutoId()));
    },

    
    getItemId : function() {
        return this.itemId || this.id;
    },

    
    getEl : function() {
        return this.el;
    },

    
    getTargetEl: function() {
        return this.frameBody || this.el;
    },
    
    
    getOverflowEl: function(){
        return this.getTargetEl();
    },

    
    getOverflowStyle: function() {
        var me = this,
            result = null,
            auto = me.autoScroll,
            ox, oy,
            overflowStyle;

        
        
        
        if (typeof auto === 'boolean') {
            result = {
                overflow: overflowStyle = (auto ? 'auto' : '')
            };
            me.scrollFlags = {
                overflowX: overflowStyle,
                overflowY: overflowStyle,
                x: auto,
                y: auto,
                both: auto
            };
        } else {
            ox = me.overflowX;
            oy = me.overflowY;
            if (ox !== undefined || oy !== undefined) {
                if (ox && ox === true) {
                    ox = 'auto';
                }
                
                if (oy && oy === true) {
                    oy = 'auto';
                }
                result = {
                    'overflowX':  ox = ox || '',
                    'overflowY':  oy = oy || ''
                };

                
                me.scrollFlags = {
                    overflowX: ox,
                    overflowY: oy,
                    x: ox = (ox === 'auto' || ox === 'scroll'),
                    y: oy = (oy === 'auto' || oy === 'scroll'),
                    both: ox && oy
                };
            } else {
                me.scrollFlags = {
                    overflowX: '',
                    overflowY: '',
                    x: false,
                    y: false,
                    both: false
                };
            }
        }

        
        
        if (result && Ext.isIE7m) {
            result.position = 'relative';
        }

        return result;
    },

    
    isXType: function(xtype, shallow) {
        if (shallow) {
            return this.xtype === xtype;
        }
        else {
            return this.xtypesMap[xtype];
        }
    },

    
    getXTypes: function() {
        var self = this.self,
            xtypes, parentPrototype, parentXtypes;

        if (!self.xtypes) {
            xtypes = [];
            parentPrototype = this;

            while (parentPrototype) {
                parentXtypes = parentPrototype.xtypes;

                if (parentXtypes !== undefined) {
                    xtypes.unshift.apply(xtypes, parentXtypes);
                }

                parentPrototype = parentPrototype.superclass;
            }

            self.xtypeChain = xtypes;
            self.xtypes = xtypes.join('/');
        }

        return self.xtypes;
    },

    
    update : function(htmlOrData, loadScripts, cb) {
        var me = this,
            isData = (me.tpl && !Ext.isString(htmlOrData)),
            el;

        if (isData) {
            me.data = htmlOrData;
        } else {
            me.html = Ext.isObject(htmlOrData) ? Ext.DomHelper.markup(htmlOrData) : htmlOrData;
        }

        if (me.rendered) {
            el = me.isContainer ? me.layout.getRenderTarget() : me.getTargetEl();
            if (isData) {
                me.tpl[me.tplWriteMode](el, htmlOrData || {});
            } else {
                el.update(me.html, loadScripts, cb);
            }
            me.updateLayout();
        }

    },

    
    setVisible : function(visible) {
        return this[visible ? 'show': 'hide']();
    },

    
    isVisible: function(deep) {
        var me = this,
            hidden;

        if (me.hidden || !me.rendered || me.isDestroyed) {
            hidden = true;
        } else if (deep) {
            hidden = me.isHierarchicallyHidden();
        }

        return !hidden;
    },

    isHierarchicallyHidden: function() {
        var child = this,
            hidden = false,
            parent, parentHierarchyState;

        
        
        
        
        for (; (parent = child.ownerCt || child.floatParent); child = parent) {
            parentHierarchyState = parent.getHierarchyState();
            if (parentHierarchyState.hidden) {
                hidden = true;
                break;
            }
            if (child.getHierarchyState().collapseImmune) {
                
                if (parent.collapsed && !child.collapseImmune) {
                    
                    
                    
                    hidden = true;
                    break;
                }
            } else {
                
                
                
                hidden = !!parentHierarchyState.collapsed;
                break;
            }
        }

        return hidden;
    },

    
    onBoxReady: function(width, height) {
        var me = this;

        if (me.disableOnBoxReady) {
            me.onDisable();
        } else if (me.enableOnBoxReady) {
            me.onEnable();
        }
        if (me.resizable) {
            me.initResizable(me.resizable);
        }

        
        
        if (me.draggable) {
            me.initDraggable();
        }
        
        if (me.hasListeners.boxready) {
            me.fireEvent('boxready', me, width, height);
        }
    },

    
    enable: function(silent) {
        var me = this;

        delete me.disableOnBoxReady;
        me.removeCls(me.disabledCls);
        if (me.rendered) {
            me.onEnable();
        } else {
            me.enableOnBoxReady = true;
        }

        me.disabled = false;
        delete me.resetDisable;

        if (silent !== true) {
            me.fireEvent('enable', me);
        }

        return me;
    },

    
    disable: function(silent) {
        var me = this;

        delete me.enableOnBoxReady;
        me.addCls(me.disabledCls);
        if (me.rendered) {
            me.onDisable();
        } else {
            me.disableOnBoxReady = true;
        }

        me.disabled = true;

        if (silent !== true) {
            delete me.resetDisable;
            me.fireEvent('disable', me);
        }

        return me;
    },

    
    onEnable: function() {
        if (this.maskOnDisable) {
            this.el.dom.disabled = false;
            this.unmask();
        }
    },

    
    onDisable : function() {
        var me = this,
            focusCls = me.focusCls,
            focusEl = me.getFocusEl();
            
        if (focusCls && focusEl) {
            focusEl.removeCls(me.removeClsWithUI(focusCls, true));
        }
        
        if (me.maskOnDisable) {
            me.el.dom.disabled = true;
            me.mask();
        }
    },

    mask: function (msg, msgCls, elHeight) {
        var box = this.lastBox,
            target = this.getMaskTarget();

        
        if (box) {
            elHeight = box.height;
        }
        target.mask(msg, msgCls, elHeight);
    },

    unmask: function() {
        this.getMaskTarget().unmask();
    },

    getMaskTarget: function(){
        return this.el;
    },

    
    isDisabled : function() {
        return this.disabled;
    },

    
    setDisabled : function(disabled) {
        return this[disabled ? 'disable': 'enable']();
    },

    
    isHidden : function() {
        return this.hidden;
    },

    
    addCls : function(cls) {
        var me = this,
            el = me.rendered ? me.el : me.protoEl;

        el.addCls.apply(el, arguments);
        return me;
    },

    
    addClass : function() {
        return this.addCls.apply(this, arguments);
    },

    
    hasCls: function (cls) {
        var me = this,
            el = me.rendered ? me.el : me.protoEl;

        return el.hasCls.apply(el, arguments);
    },

    
    removeCls : function(cls) {
        var me = this,
            el = me.rendered ? me.el : me.protoEl;

        el.removeCls.apply(el, arguments);
        return me;
    },


    addOverCls: function() {
        var me = this;
        if (!me.disabled) {
            me.el.addCls(me.overCls);
        }
    },

    removeOverCls: function() {
        this.el.removeCls(this.overCls);
    },

    addListener : function(element, listeners, scope, options) {
        var me = this,
            fn,
            option;

        if (Ext.isString(element) && (Ext.isObject(listeners) || options && options.element)) {
            if (options.element) {
                fn = listeners;

                listeners = {};
                listeners[element] = fn;
                element = options.element;
                if (scope) {
                    listeners.scope = scope;
                }

                for (option in options) {
                    if (options.hasOwnProperty(option)) {
                        if (me.eventOptionsRe.test(option)) {
                            listeners[option] = options[option];
                        }
                    }
                }
            }

            
            
            if (me[element] && me[element].on) {
                me.mon(me[element], listeners);
            } else {
                me.afterRenderEvents = me.afterRenderEvents || {};
                if (!me.afterRenderEvents[element]) {
                    me.afterRenderEvents[element] = [];
                }
                me.afterRenderEvents[element].push(listeners);
            }
            return;
        }

        return me.mixins.observable.addListener.apply(me, arguments);
    },

    
    removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope){
        var me = this,
            element = managedListener.options ? managedListener.options.element : null;

        if (element) {
            element = me[element];
            if (element && element.un) {
                if (isClear || (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope))) {
                    element.un(managedListener.ename, managedListener.fn, managedListener.scope);
                    if (!isClear) {
                        Ext.Array.remove(me.managedListeners, managedListener);
                    }
                }
            }
        } else {
            return me.mixins.observable.removeManagedListenerItem.apply(me, arguments);
        }
    },

    
    getBubbleTarget : function() {
        return this.ownerCt;
    },

    
    isFloating : function() {
        return this.floating;
    },

    
    isDraggable : function() {
        return !!this.draggable;
    },

    
    isDroppable : function() {
        return !!this.droppable;
    },

    
    onAdded : function(container, pos) {
        var me = this;

        me.ownerCt = container;

        if (me.hierarchyState) {
            
            
            
            me.hierarchyState.invalid = true;
            
            
            
            
            
            delete me.hierarchyState;
        }

        if (me.hasListeners.added) {
            me.fireEvent('added', me, container, pos);
        }
    },

    
    onRemoved : function(destroying) {
        var me = this;
        if (me.hasListeners.removed) {
            me.fireEvent('removed', me, me.ownerCt);
        }
        delete me.ownerCt;
        delete me.ownerLayout;
    },

    
    beforeDestroy : Ext.emptyFn,

    
    onResize: function(width, height, oldWidth, oldHeight) {
        var me = this;

        
        if (me.floating && me.constrain) {
            me.doConstrain();
        }
        if (me.hasListeners.resize) {
            me.fireEvent('resize', me, width, height, oldWidth, oldHeight);
        }
    },

    
    setSize : function(width, height) {
        var me = this;

        
        if (width && typeof width == 'object') {
            height = width.height;
            width  = width.width;
        }

        
        if (typeof width == 'number') {
            me.width = Ext.Number.constrain(width, me.minWidth, me.maxWidth);
        } else if (width === null) {
            delete me.width;
        }
        
        if (typeof height == 'number') {
            me.height = Ext.Number.constrain(height, me.minHeight, me.maxHeight);
        } else if (height === null) {
            delete me.height;
        }

        
        
        if (me.rendered && me.isVisible()) {

            
            me.updateLayout({
                isRoot: false
            });
        }

        return me;
    },

    
    isLayoutRoot: function() {
        var me = this,
            ownerLayout = me.ownerLayout;

        
        
        
        
        if (!ownerLayout || me._isLayoutRoot || me.floating) {
            return true;
        }

        return ownerLayout.isItemLayoutRoot(me);
    },

    
    isLayoutSuspended: function () {
        var comp = this,
            ownerLayout;

        while (comp) {
            if (comp.layoutSuspendCount || comp.suspendLayout) {
                return true;
            }

            ownerLayout = comp.ownerLayout;
            if (!ownerLayout) {
                break;
            }

            

            
            
            
            comp = ownerLayout.owner;
        }

        return false;
    },

    
    updateLayout: function (options) {
        var me = this,
            defer,
            lastBox = me.lastBox,
            isRoot = options && options.isRoot;

        if (lastBox) {
            
            
            lastBox.invalid = true;
        }

        if (!me.rendered || me.layoutSuspendCount || me.suspendLayout) {
            return;
        }

        if (me.hidden) {
            Ext.AbstractComponent.cancelLayout(me);
        } else if (typeof isRoot != 'boolean') {
            isRoot = me.isLayoutRoot();
        }

        
        if (isRoot || !me.ownerLayout || !me.ownerLayout.onContentChange(me)) {
            
            if (!me.isLayoutSuspended()) {
                
                defer = (options && options.hasOwnProperty('defer')) ? options.defer : me.deferLayouts;
                Ext.AbstractComponent.updateLayout(me, defer);
            }
        }
    },

    
    getSizeModel: function (ownerCtSizeModel) {
        var me = this,
            models = Ext.layout.SizeModel,
            ownerContext = me.componentLayout.ownerContext,
            width = me.width,
            height = me.height,
            typeofWidth, typeofHeight,
            hasPixelWidth, hasPixelHeight,
            heightModel, ownerLayout, policy, shrinkWrap, topLevel, widthModel;

        if (ownerContext) {
            
            
            
            
            widthModel = ownerContext.widthModel;
            heightModel = ownerContext.heightModel;
        }

        if (!widthModel || !heightModel) {
            hasPixelWidth = ((typeofWidth = typeof width) == 'number');
            hasPixelHeight = ((typeofHeight = typeof height) == 'number');
            topLevel = me.floating || !(ownerLayout = me.ownerLayout);

            
            if (topLevel) {
                policy = Ext.layout.Layout.prototype.autoSizePolicy;
                shrinkWrap = me.floating ? 3 : me.shrinkWrap;

                if (hasPixelWidth) {
                    widthModel = models.configured;
                }

                if (hasPixelHeight) {
                    heightModel = models.configured;
                }
            } else {
                policy = ownerLayout.getItemSizePolicy(me, ownerCtSizeModel);
                shrinkWrap = ownerLayout.isItemShrinkWrap(me);
            }

            if (ownerContext) {
                ownerContext.ownerSizePolicy = policy;
            }

            shrinkWrap = (shrinkWrap === true) ? 3 : (shrinkWrap || 0); 

            
            
            
            
            if (topLevel && shrinkWrap) {
                if (width && typeofWidth == 'string') {
                    shrinkWrap &= 2; 
                }
                if (height && typeofHeight == 'string') {
                    shrinkWrap &= 1; 
                }
            }

            if (shrinkWrap !== 3) {
                if (!ownerCtSizeModel) {
                    ownerCtSizeModel = me.ownerCt && me.ownerCt.getSizeModel();
                }

                if (ownerCtSizeModel) {
                    shrinkWrap |= (ownerCtSizeModel.width.shrinkWrap ? 1 : 0) | (ownerCtSizeModel.height.shrinkWrap ? 2 : 0);
                }
            }

            if (!widthModel) {
                if (!policy.setsWidth) {
                    if (hasPixelWidth) {
                        widthModel = models.configured;
                    } else {
                        widthModel = (shrinkWrap & 1) ? models.shrinkWrap : models.natural;
                    }
                } else if (policy.readsWidth) {
                    if (hasPixelWidth) {
                        widthModel = models.calculatedFromConfigured;
                    } else {
                        widthModel = (shrinkWrap & 1) ? models.calculatedFromShrinkWrap :
                                    models.calculatedFromNatural;
                    }
                } else {
                    widthModel = models.calculated;
                }
            }

            if (!heightModel) {
                if (!policy.setsHeight) {
                    if (hasPixelHeight) {
                        heightModel = models.configured;
                    } else {
                        heightModel = (shrinkWrap & 2) ? models.shrinkWrap : models.natural;
                    }
                } else if (policy.readsHeight) {
                    if (hasPixelHeight) {
                        heightModel = models.calculatedFromConfigured;
                    } else {
                        heightModel = (shrinkWrap & 2) ? models.calculatedFromShrinkWrap :
                                    models.calculatedFromNatural;
                    }
                } else {
                    heightModel = models.calculated;
                }
            }
        }

        
        
        return widthModel.pairsByHeightOrdinal[heightModel.ordinal];
    },

    isDescendant: function(ancestor) {
        if (ancestor.isContainer) {
            for (var c = this.ownerCt; c; c = c.ownerCt) {
                if (c === ancestor) {
                    return true;
                }
            }
        }
        return false;
    },

    
    doComponentLayout : function() {
        this.updateLayout();
        return this;
    },

    
    forceComponentLayout: function () {
        this.updateLayout();
    },

    
    setComponentLayout : function(layout) {
        var currentLayout = this.componentLayout;
        if (currentLayout && currentLayout.isLayout && currentLayout != layout) {
            currentLayout.setOwner(null);
        }
        this.componentLayout = layout;
        layout.setOwner(this);
    },

    getComponentLayout : function() {
        var me = this;

        if (!me.componentLayout || !me.componentLayout.isLayout) {
            me.setComponentLayout(Ext.layout.Layout.create(me.componentLayout, 'autocomponent'));
        }
        return me.componentLayout;
    },

    
    afterComponentLayout: function(width, height, oldWidth, oldHeight) {
        var me = this;

        if (++me.componentLayoutCounter === 1) {
            me.afterFirstLayout(width, height);
        }

        if (width !== oldWidth || height !== oldHeight) {
            me.onResize(width, height, oldWidth, oldHeight);
        }
    },

    
    beforeComponentLayout: function(width, height) {
        return true;
    },

    
    setPosition: function(x, y, animate) {
        var me = this,
            pos = me.beforeSetPosition.apply(me, arguments);

        if (pos && me.rendered) {
            x = pos.x;
            y = pos.y;

            if (animate) {
                
                
                
                if (x !== me.getLocalX() || y !== me.getLocalY()) {
                    me.stopAnimation();
                    me.animate(Ext.apply({
                        duration: 1000,
                        listeners: {
                            afteranimate: Ext.Function.bind(me.afterSetPosition, me, [x, y])
                        },
                        to: {
                            
                            
                            
                            
                            left: x,
                            top: y
                        }
                    }, animate));
                }
            } else {
                me.setLocalXY(x, y);
                me.afterSetPosition(x, y);
            }
        }
        return me;
    },

    
    beforeSetPosition: function (x, y, animate) {
        var pos, x0;

        
        
        if (x) {
            
            if (Ext.isNumber(x0 = x[0])) {
                animate = y;
                y = x[1];
                x = x0;
            }
            
            else if ((x0 = x.x) !== undefined) {
                animate = y;
                y = x.y;
                x = x0;
            }
        }

        if (this.constrain || this.constrainHeader) {
            pos = this.calculateConstrainedPosition(null, [x, y], true);
            if (pos) {
                x = pos[0];
                y = pos[1];
            }
        }

        
        pos = {
            x : this.x = x,
            y : this.y = y,
            anim: animate,
            hasX: x !== undefined,
            hasY: y !== undefined
        };

        return (pos.hasX || pos.hasY) ? pos : null;
    },

    
    afterSetPosition: function(x, y) {
        var me = this;
        me.onPosition(x, y);
        if (me.hasListeners.move) {
            me.fireEvent('move', me, x, y);
        }
    },

    
    onPosition: Ext.emptyFn,

    
    setWidth : function(width) {
        return this.setSize(width);
    },

    
    setHeight : function(height) {
        return this.setSize(undefined, height);
    },

    
    getSize : function(contentSize) {
        return this.el.getSize(contentSize);
    },

    
    getWidth : function() {
        return this.el.getWidth();
    },

    
    getHeight : function() {
        return this.el.getHeight();
    },

    
    getLoader: function(){
        var me = this,
            autoLoad = me.autoLoad ? (Ext.isObject(me.autoLoad) ? me.autoLoad : {url: me.autoLoad}) : null,
            loader = me.loader || autoLoad;

        if (loader) {
            if (!loader.isLoader) {
                me.loader = new Ext.ComponentLoader(Ext.apply({
                    target: me,
                    autoLoad: autoLoad
                }, loader));
            } else {
                loader.setTarget(me);
            }
            return me.loader;

        }
        return null;
    },

    
    setDocked : function(dock, layoutParent) {
        var me = this;

        me.dock = dock;
        if (layoutParent && me.ownerCt && me.rendered) {
            me.ownerCt.updateLayout();
        }
        return me;
    },

    
    setBorder: function(border,  targetEl) {
        var me = this,
            initial = !!targetEl;

        if (me.rendered || initial) {
            if (!initial) {
                targetEl = me.el;
            }

            if (!border) {
                border = 0;
            } else if (border === true) {
                border = '1px';
            } else {
                border = this.unitizeBox(border);
            }
            targetEl.setStyle('border-width', border);
            if (!initial) {
                me.updateLayout();
            }
        }
        me.border = border;
    },

    onDestroy : function() {
        var me = this;

        if (me.monitorResize && Ext.EventManager.resizeEvent) {
            Ext.EventManager.resizeEvent.removeListener(me.setSize, me);
        }

        
        Ext.destroy(
            me.componentLayout,
            me.loadMask,
            me.floatingDescendants
        );
    },

    
    destroy : function() {
        var me = this,
            selectors = me.renderSelectors,
            selector,
            el;

        if (!me.isDestroyed) {
            if (!me.hasListeners.beforedestroy || me.fireEvent('beforedestroy', me) !== false) {
                me.destroying = true;
                me.beforeDestroy();

                if (me.floating) {
                    delete me.floatParent;
                    
                    
                    if (me.zIndexManager) {
                        me.zIndexManager.unregister(me);
                    }
                } else if (me.ownerCt && me.ownerCt.remove) {
                    me.ownerCt.remove(me, false);
                }

                me.stopAnimation();
                me.onDestroy();

                
                Ext.destroy(me.plugins);

                if (me.hasListeners.destroy) {
                    me.fireEvent('destroy', me);
                }
                Ext.ComponentManager.unregister(me);

                me.mixins.state.destroy.call(me);

                me.clearListeners();
                
                if (me.rendered) {
                    if (!me.preserveElOnDestroy) {
                        me.el.remove();
                    }
                    me.mixins.elementCt.destroy.call(me); 
                    if (selectors) {
                        for (selector in selectors) {
                            if (selectors.hasOwnProperty(selector)) {
                                el = me[selector];
                                if (el) { 
                                    delete me[selector];
                                    el.remove();
                                }
                            }
                        }
                    }

                    delete me.el;
                    delete me.frameBody;
                    delete me.rendered;
                }

                me.destroying = false;
                me.isDestroyed = true;
            }
        }
    },

    
    isDescendantOf: function(container) {
        return !!this.findParentBy(function(p){
            return p === container;
        });
    },

    
    getHierarchyState: function (inner) {
        var me = this,
            hierarchyState = (inner && me.hierarchyStateInner) || me.hierarchyState,
            ownerCt = me.ownerCt,
            parent, layout, hierarchyStateInner, getInner;
 
        if (!hierarchyState || hierarchyState.invalid) {
            
            
            
            
            parent = me.getRefOwner();
            
            if (ownerCt) {
                
                
                getInner = me.ownerLayout === ownerCt.layout;
            }

            me.hierarchyState = hierarchyState =
                
                
                
                
                
                Ext.Object.chain(parent ? parent.getHierarchyState(getInner)
                                        : Ext.rootHierarchyState);

            me.initHierarchyState(hierarchyState);
            if ((layout = me.componentLayout).initHierarchyState) {
                layout.initHierarchyState(hierarchyState);
            }

            if (me.isContainer) {
                me.hierarchyStateInner = hierarchyStateInner = Ext.Object.chain(hierarchyState);

                layout = me.layout;
                if (layout && layout.initHierarchyState) {
                    layout.initHierarchyState(hierarchyStateInner, hierarchyState);
                }
                if (inner) {
                    hierarchyState = hierarchyStateInner;
                }
            }
        }

        return hierarchyState;
    },

    
    initHierarchyState: function(hierarchyState) {
        var me = this;

        if (me.collapsed) {
            hierarchyState.collapsed = true;
        }
        if (me.hidden) {
            hierarchyState.hidden = true;
        }
        if (me.collapseImmune) {
            hierarchyState.collapseImmune = true;
        }
    },

    
    
    

    getAnchorToXY: function(el, anchor, local, mySize) {
        return el.getAnchorXY(anchor, local, mySize);
    },

    getBorderPadding: function() {
        return this.el.getBorderPadding();
    },

    getLocalX: function() {
        return this.el.getLocalX();
    },

    getLocalXY: function() {
        return this.el.getLocalXY();
    },

    getLocalY: function() {
        return this.el.getLocalY();
    },

    getX: function() {
        return this.el.getX();
    },

    getXY: function() {
        return this.el.getXY();
    },

    getY: function() {
        return this.el.getY();
    },

    setLocalX: function(x) {
        this.el.setLocalX(x);
    },

    setLocalXY: function(x, y) {
        this.el.setLocalXY(x, y);
    },

    setLocalY: function(y) {
        this.el.setLocalY(y);
    },

    setX: function(x, animate) {
        this.el.setX(x, animate);
    },

    setXY: function(xy, animate) {
        this.el.setXY(xy, animate);
    },

    setY: function(y, animate) {
        this.el.setY(y, animate);
    }

    
    
    
}, function() {
    var AbstractComponent = this;

    AbstractComponent.createAlias({
        on: 'addListener',
        prev: 'previousSibling',
        next: 'nextSibling'
    });

    
    Ext.resumeLayouts = function (flush) {
        AbstractComponent.resumeLayouts(flush);
    };

    
    Ext.suspendLayouts = function () {
        AbstractComponent.suspendLayouts();
    };

    
    Ext.batchLayouts = function(fn, scope) {
        AbstractComponent.suspendLayouts();
        
        fn.call(scope);
        AbstractComponent.resumeLayouts(true);
    };
});


Ext.define('Ext.AbstractPlugin', {
    disabled: false,

    
    isPlugin: true,

    
    constructor: function(config) {
        this.pluginConfig = config;
        Ext.apply(this, 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: Ext.emptyFn,

    
    enable: function() {
        this.disabled = false;
    },

    
    disable: function() {
        this.disabled = true;
    },

    
    
    onClassExtended: function(cls, data, hooks) {
        var alias = data.alias;

        
        if (alias && !data.ptype) {
            if (Ext.isArray(alias)) {
                alias = alias[0];
            }
            cls.prototype.ptype = alias.split('plugin.')[1];
        }
    }
});


Ext.define('Ext.Action', {

    

    

    
    
    
    
    
    
    

    
    constructor : function(config){
        this.initialConfig = config;
        this.itemId = config.itemId = (config.itemId || config.id || Ext.id());
        this.items = [];
    },

    
    isAction : true,

    
    setText : function(text){
        this.initialConfig.text = text;
        this.callEach('setText', [text]);
    },

    
    getText : function(){
        return this.initialConfig.text;
    },

    
    setIconCls : function(cls){
        this.initialConfig.iconCls = cls;
        this.callEach('setIconCls', [cls]);
    },

    
    getIconCls : function(){
        return this.initialConfig.iconCls;
    },

    
    setDisabled : function(v){
        this.initialConfig.disabled = v;
        this.callEach('setDisabled', [v]);
    },

    
    enable : function(){
        this.setDisabled(false);
    },

    
    disable : function(){
        this.setDisabled(true);
    },

    
    isDisabled : function(){
        return this.initialConfig.disabled;
    },

    
    setHidden : function(v){
        this.initialConfig.hidden = v;
        this.callEach('setVisible', [!v]);
    },

    
    show : function(){
        this.setHidden(false);
    },

    
    hide : function(){
        this.setHidden(true);
    },

    
    isHidden : function(){
        return this.initialConfig.hidden;
    },

    
    setHandler : function(fn, scope){
        this.initialConfig.handler = fn;
        this.initialConfig.scope = scope;
        this.callEach('setHandler', [fn, scope]);
    },

    
    each : function(fn, scope){
        Ext.each(this.items, fn, scope);
    },

    
    callEach : function(fnName, args){
        var items = this.items,
            i = 0,
            len = items.length,
            item;

        Ext.suspendLayouts();
        for(; i < len; i++){
            item = items[i];
            item[fnName].apply(item, args);
        }
        Ext.resumeLayouts(true);
    },

    
    addComponent : function(comp){
        this.items.push(comp);
        comp.on('destroy', this.removeComponent, this);
    },

    
    removeComponent : function(comp){
        Ext.Array.remove(this.items, comp);
    },

    
    execute : function(){
        this.initialConfig.handler.apply(this.initialConfig.scope || Ext.global, arguments);
    }
});


Ext.define('Ext.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);
            } 
        },
        
        
        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";
            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() {
                },
                scope: me
            });
            Ext.globalEvents.addEvents("flashready"); 
            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) {
            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('\x0d\x0a');
    },

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

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

    
    onFlashStateChange: function(state, data) {
        var me = this;
        if (state == 4) {
            
            me.parseData(data);
            
            delete Ext.data.flash.BinaryXhr.liveConnections[me.javascriptId];
        }
        me.setReadyState(state); 
    }
    
});


Ext.define('Ext.data.Connection', {
    mixins: {
        observable:  Ext.util.Observable 
    },
    
               
                                  
      

    statics: {
        requestId: 0
    },

    url: null,
    async: true,
    method: null,
    username: '',
    password: '',

    
    disableCaching: true,

    
    withCredentials: false,

    
    binary: false,

    
    cors: false,

    isXdr: false,

    defaultXdrContentType: 'text/plain',

    
    disableCachingParam: '_dc',

    
    timeout : 30000,

    

    

    

    

    useDefaultHeader : true,
    defaultPostHeader : 'application/x-www-form-urlencoded; charset=UTF-8',
    useDefaultXhrHeader : true,
    defaultXhrHeader : 'XMLHttpRequest',

    constructor : function(config) {
        config = config || {};
        Ext.apply(this, config);

        
        
        
        this.requests = {};
        this.mixins.observable.constructor.call(this);
    },

    
    request : function(options) {
        options = options || {};
        var me = this,
            scope = options.scope || window,
            username = options.username || me.username,
            password = options.password || me.password || '',
            async,
            requestOptions,
            request,
            headers,
            xhr;
        if (me.fireEvent('beforerequest', me, options) !== false) {

            requestOptions = me.setOptions(options, scope);

            if (me.isFormUpload(options)) {
                me.upload(options.form, requestOptions.url, requestOptions.data, options);
                return null;
            }

            
            if (options.autoAbort || me.autoAbort) {
                me.abort();
            }

            
            async = options.async !== false ? (options.async || me.async) : false;
            xhr = me.openRequest(options, requestOptions, async, username, password);

            
            if (!me.isXdr) {
                headers = me.setupHeaders(xhr, options, requestOptions.data, requestOptions.params);
            }

            
            request = {
                id: ++Ext.data.Connection.requestId,
                xhr: xhr,
                headers: headers,
                options: options,
                async: async,
                binary: options.binary || me.binary,
                timeout: setTimeout(function() {
                    request.timedout = true;
                    me.abort(request);
                }, options.timeout || me.timeout)
            };

            me.requests[request.id] = request;
            me.latestId = request.id;
            
            if (async) {
                if (!me.isXdr) {
                    xhr.onreadystatechange = Ext.Function.bind(me.onStateChange, me, [request]);
                }
            }

            if (me.isXdr) {
                me.processXdrRequest(request, xhr);
            }

            
            xhr.send(requestOptions.data);
            if (!async) {
                return me.onComplete(request);
            }
            return request;
        } else {
            Ext.callback(options.callback, options.scope, [options, undefined, undefined]);
            return null;
        }
    },

    processXdrRequest: function(request, xhr) {
        var me = this;

        
        delete request.headers;

        request.contentType = request.options.contentType || me.defaultXdrContentType;

        xhr.onload = Ext.Function.bind(me.onStateChange, me, [request, true]);
        xhr.onerror = xhr.ontimeout = Ext.Function.bind(me.onStateChange, me, [request, false]);
    },

    processXdrResponse: function(response, xhr) {
        
        response.getAllResponseHeaders = function () {
            return [];
        };
        response.getResponseHeader = function () {
            return '';
        };
        response.contentType = xhr.contentType || this.defaultXdrContentType;
    },

    
    upload: function(form, url, params, options) {
        form = Ext.getDom(form);
        options = options || {};

        var id = Ext.id(),
            frame = document.createElement('iframe'),
            hiddens = [],
            encoding = 'multipart/form-data',
            buf = {
                target: form.target,
                method: form.method,
                encoding: form.encoding,
                enctype: form.enctype,
                action: form.action
            },
            addField = function(name, value) {
                hiddenItem = document.createElement('input');
                Ext.fly(hiddenItem).set({
                    type: 'hidden',
                    value: value,
                    name: name
                });
                form.appendChild(hiddenItem);
                hiddens.push(hiddenItem);
            },
            hiddenItem, obj, value, name, vLen, v, hLen, h;

        
        Ext.fly(frame).set({
            id: id,
            name: id,
            cls: Ext.baseCSSPrefix + 'hide-display',
            src: Ext.SSL_SECURE_URL
        });

        document.body.appendChild(frame);

        
        if (document.frames) {
            document.frames[id].name = id;
        }

        Ext.fly(form).set({
            target: id,
            method: 'POST',
            enctype: encoding,
            encoding: encoding,
            action: url || buf.action
        });

        
        if (params) {
            obj = Ext.Object.fromQueryString(params) || {};

            for (name in obj) {
                if (obj.hasOwnProperty(name)) {
                    value = obj[name];  
                    if (Ext.isArray(value)) {
                        vLen = value.length;
                        for (v = 0; v < vLen; v++) {
                            addField(name, value[v]);
                        }
                    } else {
                        addField(name, value);
                    }
                }
            }
        }

        Ext.fly(frame).on('load', Ext.Function.bind(this.onUploadComplete, this, [frame, options]), null, {single: !Ext.isOpera});
        form.submit();

        Ext.fly(form).set(buf);

        hLen = hiddens.length;

        for (h = 0; h < hLen; h++) {
            Ext.removeNode(hiddens[h]);
        }
    },

    
    onUploadComplete: function(frame, options) {
        var me = this,
            
            response = {
                responseText: '',
                responseXML: null
            }, callback, success, doc, contentNode;

        try {
            doc = frame.contentWindow.document || frame.contentDocument || window.frames[frame.id].document;
            
            
            if (doc) {
                 if (Ext.isOpera && doc.location == 'about:blank') {
                     return;
                 }
                if (doc.body) {

                    
                    
                    if ((contentNode = doc.body.firstChild) && /pre/i.test(contentNode.tagName)) {
                        response.responseText = contentNode.textContent;
                    }

                    
                    
                    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;
            }
        } catch (e) {
            
            response.responseText = '{success:false,message:"' + Ext.String.trim(e.message || e.description) + '"}';
            callback = options.failure;
            success = false;
        }

        me.fireEvent('requestcomplete', me, response, options);

        Ext.callback(callback, options.scope, [response, options]);
        Ext.callback(options.callback, options.scope, [options, success, response]);

        setTimeout(function() {
            Ext.removeNode(frame);
        }, 100);
    },

    
    isFormUpload: function(options) {
        var form = this.getForm(options);
        if (form) {
            return (options.isUpload || (/multipart\/form-data/i).test(form.getAttribute('enctype')));
        }
        return false;
    },

    
    getForm: function(options) {
        return Ext.getDom(options.form) || null;
    },

    
    setOptions: function(options, scope) {
        var me = this,
            params = options.params || {},
            extraParams = me.extraParams,
            urlParams = options.urlParams,
            url = options.url || me.url,
            jsonData = options.jsonData,
            method,
            disableCache,
            data;


        
        if (Ext.isFunction(params)) {
            params = params.call(scope, options);
        }

        
        if (Ext.isFunction(url)) {
            url = url.call(scope, options);
        }

        url = this.setupUrl(options, url);


        
        data = options.rawData || options.binaryData || options.xmlData || jsonData || null;
        if (jsonData && !Ext.isPrimitive(jsonData)) {
            data = Ext.encode(data);
        }
        
        if (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.method || ((params || data) ? 'POST' : 'GET')).toUpperCase();
        this.setupMethod(options, method);


        disableCache = options.disableCaching !== false ? (options.disableCaching || me.disableCaching) : false;
        
        if (method === 'GET' && disableCache) {
            url = Ext.urlAppend(url, (options.disableCachingParam || me.disableCachingParam) + '=' + (new Date().getTime()));
        }

        
        if ((method == 'GET' || data) && params) {
            url = Ext.urlAppend(url, params);
            params = null;
        }

        
        if (urlParams) {
            url = Ext.urlAppend(url, urlParams);
        }

        return {
            url: url,
            method: method,
            data: data || params || null
        };
    },

    
    setupUrl: function(options, url) {
        var form = this.getForm(options);
        if (form) {
            url = url || form.action;
        }
        return url;
    },


    
    setupParams: function(options, params) {
        var form = this.getForm(options),
            serializedForm;
        if (form && !this.isFormUpload(options)) {
            serializedForm = Ext.Element.serializeForm(form);
            params = params ? (params + '&' + serializedForm) : serializedForm;
        }
        return params;
    },

    
    setupMethod: function(options, method) {
        if (this.isFormUpload(options)) {
            return 'POST';
        }
        return method;
    },

    
    setupHeaders: function(xhr, options, data, params) {
        var me = this,
            headers = Ext.apply({}, options.headers || {}, me.defaultHeaders || {}),
            contentType = me.defaultPostHeader,
            jsonData = options.jsonData,
            xmlData = options.xmlData,
            type = 'Content-Type',
            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 (me.useDefaultXhrHeader && !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.fireEvent('exception', key, header);
        }
        return headers;
    },

    
    newRequest: function (options) {
        var me = this,
            xhr;

        if (options.binaryData) {
            
            if (me.nativeBinaryPostSupport()) { 
                xhr = this.getXhrInstance(); 
            } else {
                
                xhr = new Ext.data.flash.BinaryXhr();
            }
        } else  if ((options.cors || me.cors) && Ext.isIE && Ext.ieVersion <= 9) {
            xhr = me.getXdrInstance();
            me.isXdr = true;
        } else {
            xhr = me.getXhrInstance();
        }

        return xhr;
    },

    
    openRequest: function (options, requestOptions, async, username, password) {
        var me = this,
            xhr = me.newRequest(options);

        if (username) {
            xhr.open(requestOptions.method, requestOptions.url, async, username, password);
        } else {
            if (me.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');  
            }
        }

        if (options.withCredentials || me.withCredentials) {
            xhr.withCredentials = true;
        }

        return xhr;
    },

    
    getXdrInstance: function() {
        var xdr;

        if (Ext.ieVersion >= 8) {
            xdr = new XDomainRequest();
        } else {
            Ext.Error.raise({
                msg: 'Your browser does not support CORS'
            });
        }

        return xdr;
    },

    
    getXhrInstance: (function() {
        var options = [function() {
            return new XMLHttpRequest();
        }, function() {
            return new ActiveXObject('MSXML2.XMLHTTP.3.0');
        }, function() {
            return new ActiveXObject('MSXML2.XMLHTTP');
        }, function() {
            return new ActiveXObject('Microsoft.XMLHTTP');
        }], i = 0,
            len = options.length,
            xhr;

        for (; i < len; ++i) {
            try {
                xhr = options[i];
                xhr();
                break;
            } catch(e) {
            }
        }
        return xhr;
    }()),

    
    isLoading : function(request) {
        if (!request) {
            request = this.getLatest();
        }
        if (!(request && request.xhr)) {
            return false;
        }
        
        var state = request.xhr.readyState;
        return ((request.xhr instanceof Ext.data.flash.BinaryXhr) && state != 4) || !(state === 0 || state == 4);
    },

    
    abort : function(request) {
        var me = this,
            xhr;
        
        if (!request) {
            request = me.getLatest();
        }

        if (request && me.isLoading(request)) {
            
            xhr = request.xhr;
            try {
                xhr.onreadystatechange = null;
            } catch (e) {
                
                
                xhr.onreadystatechange = Ext.emptyFn;
            }
            xhr.abort();
            me.clearTimeout(request);
            if (!request.timedout) {
                request.aborted = true;
            }
            me.onComplete(request);
            me.cleanup(request);
        }
    },
    
    
    abortAll: function(){
        var requests = this.requests,
            id;
        
        for (id in requests) {
            if (requests.hasOwnProperty(id)) {
                this.abort(requests[id]);
            }
        }
    },
    
    
    getLatest: function(){
        var id = this.latestId,
            request;
            
        if (id) {
            request = this.requests[id];
        }
        return request || null;
    },

    
    onStateChange : function(request, xdrResult) {
        var me = this;

        
        if ((request.xhr && request.xhr.readyState == 4) || me.isXdr) {
            me.clearTimeout(request);
            me.onComplete(request, xdrResult);
            me.cleanup(request);
            Ext.EventManager.idleEvent.fire();
        }
    },

    
    clearTimeout: function(request) {
        clearTimeout(request.timeout);
        delete request.timeout;
    },

    
    cleanup: function(request) {
        request.xhr = null;
        delete request.xhr;
    },

    
    onComplete : function(request, xdrResult) {
        var me = this,
            options = request.options,
            xhr,
            result,
            success,
            response;

        try {
            xhr = request.xhr;
            result = me.parseStatus(xhr.status);
            if (result.success) {
                
                
                
                result.success = xhr.readyState === 4;
            }
        } catch (e) {
            
            result = {
                success : false,
                isException : false
            };

        }
        success = me.isXdr ? xdrResult : result.success;

        if (success) {
            response = me.createResponse(request);
            me.fireEvent('requestcomplete', me, response, options);
            Ext.callback(options.success, options.scope, [response, options]);
        } else {
            if (result.isException || request.aborted || request.timedout) {
                response = me.createException(request);
            } else {
                response = me.createResponse(request);
            }
            me.fireEvent('requestexception', me, response, options);
            Ext.callback(options.failure, options.scope, [response, options]);
        }
        Ext.callback(options.callback, options.scope, [options, success, response]);
        delete me.requests[request.id];
        return response;
    },

    
    parseStatus: function(status) {
        
        status = status == 1223 ? 204 : status;

        var success = (status >= 200 && status < 300) || status == 304,
            isException = false;

        if (!success) {
            switch (status) {
                case 12002:
                case 12029:
                case 12030:
                case 12031:
                case 12152:
                case 13030:
                    isException = true;
                    break;
            }
        }
        return {
            success: success,
            isException: isException
        };
    },

    
    createResponse : function(request) {
        var me = this,
            xhr = request.xhr,
            isXdr = me.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);
            }
        }

        request.xhr = null;
        delete request.xhr;

        response = {
            request: request,
            requestId: request.id,
            status: xhr.status,
            statusText: xhr.statusText,
            getResponseHeader: function(header) {
                return headers[header.toLowerCase()];
            },
            getAllResponseHeaders: function() {
                return headers;
            }
        };

        if (isXdr) {
            me.processXdrResponse(response, xhr);
        }

        if (request.binary) {
            response.responseBytes = me.getByteArray(xhr);
        } else {
            
            
            
            
            response.responseText = xhr.responseText;
            response.responseXML = xhr.responseXML;
        }

        
        
        xhr = null;
        return response;
    },

    
    createException : function(request) {
        return {
            request : request,
            requestId : request.id,
            status : request.aborted ? -1 : 0,
            statusText : request.aborted ? 'transaction aborted' : 'communication failure',
            aborted: request.aborted,
            timedout: request.timedout
        };
    },

    
    getByteArray: function(xhr) {
        var response = xhr.response,
            responseBody = xhr.responseBody,
            byteArray, responseText, len, i;

        if (xhr instanceof Ext.data.flash.BinaryXhr) {
            
            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) & 0xFF);
            }
        }

        return byteArray;
    },

    
    injectVBScript: function() {
        var scriptTag = document.createElement('script');
        scriptTag.type = 'text/vbscript';
        scriptTag.text = [
            'Function getIEByteArray(byteArray, out)',
                'Dim len, i',
                'len = LenB(byteArray)',
                'For i = 1 to len',
                    'out.push(AscB(MidB(byteArray, i, 1)))',
                'Next',
            'End Function'
        ].join('\n');
        Ext.getHead().dom.appendChild(scriptTag);
        this.self.vbScriptInjected = true;
    },
    
    
    nativeBinaryPostSupport: function() {
        return Ext.isChrome ||
            (Ext.isSafari && Ext.isDefined(window.Uint8Array)) ||
            (Ext.isGecko && Ext.isDefined(window.Uint8Array));
    }
    
    
});


Ext.define('Ext.Ajax', {
    extend:  Ext.data.Connection ,
    singleton: true,

    
    
    
    
    
    

    
    
    
    
    
    

    
    autoAbort : false
});


Ext.define('Ext.util.Floating', {

                                             

    
    focusOnToFront: true,

    
    shadow: 'sides',

    
    constrain: false,

    

    

    constructor: function (dom) {
        var me = this;

        
        me.fixed = me.fixed && !(Ext.isIE6 || Ext.isIEQuirks);

        me.el = new Ext.dom.Layer(Ext.apply({
            preventSync  : true,
            hideMode     : me.hideMode,
            shadow       : (typeof me.shadow != 'undefined') ? me.shadow : 'sides',
            shadowOffset : me.shadowOffset,
            constrain    : false,
            fixed        : me.fixed,
            shim         : (me.shim === false) ? false : undefined
        }, me.floating), dom);

        
        
        if (me.modal && !(Ext.FocusManager && Ext.FocusManager.enabled)) {
            me.mon(me.el, {
                keydown: me.onKeyDown,
                scope: me
            });
        }

        
        me.mon(me.el, {
            mousedown: me.onMouseDown,
            scope: me
        });

        
        me.floating = true;

        
        
        
        me.registerWithOwnerCt();

        me.initHierarchyEvents();
    },

    initHierarchyEvents: function() {
        var me = this,
            syncHidden = this.syncHidden;

        if (!me.hasHierarchyEventListeners) {
            me.mon(me.hierarchyEventSource, {
                hide: syncHidden,
                collapse: syncHidden,
                show: syncHidden,
                expand: syncHidden,
                added: syncHidden,
                scope: me
            });
            me.hasHierarchyEventListeners = true;
        }
    },

    registerWithOwnerCt: function() {
        var me = this,
            ownerCt = me.ownerCt,
            zip = me.zIndexParent;

        if (zip) {
            zip.unregisterFloatingItem(me);
        }

        
        
        zip = me.zIndexParent = me.up('[floating]');

        
        
        
        me.setFloatParent(ownerCt || zip);
        delete me.ownerCt;

        if (zip) {
            zip.registerFloatingItem(me);
        } else {
            Ext.WindowManager.register(me);
        }
    },

    
    onKeyDown: function(e) {
        var me = this,
            shift,
            focusables,
            first,
            last;

        
        
        
        
        
        
        
        
        
        
        
        if (e.getKey() == Ext.EventObject.TAB) {
            shift = e.shiftKey;
            focusables = me.el.query(':focusable');
            first = focusables[0];
            last = focusables[focusables.length - 1];
            if (first && last && e.target === (shift ? first : last)) {
                e.stopEvent();
                (shift ? last : first).focus(false, true);
            }
        }
    },

    
    
    onMouseDown: function (e) {
        var focusTask = this.focusTask;
        
        if (this.floating &&
            
            
            
            (!focusTask || !focusTask.id)) {
            
            this.toFront(!!e.getTarget(':focusable'));
        }
    },

    setFloatParent: function(floatParent) {
        var me = this;

        me.floatParent = floatParent;

        
        
        if ((me.constrain || me.constrainHeader) && !me.constrainTo) {
            me.constrainTo = floatParent ? floatParent.getTargetEl() : me.container;
        }
    },
    
    
    syncShadow : function() {
        if (this.floating) {
            this.el.sync(true);
        }
    },
    
    onBeforeFloatLayout: function(){
        this.el.preventSync = true;
    },
    
    onAfterFloatLayout: function(){
        delete this.el.preventSync;
        this.syncShadow();   
    },

    
    syncHidden: function() {
        var me = this,
            hidden = me.hidden || !me.rendered,
            hierarchicallyHidden = me.hierarchicallyHidden = me.isHierarchicallyHidden(),
            pendingShow = me.pendingShow;

        if (hidden !== hierarchicallyHidden) {
            if (hierarchicallyHidden) {
                me.hide();
                me.pendingShow = true;
            } else if (pendingShow) {
                delete me.pendingShow;
                if (pendingShow.length) {
                    me.show.apply(me, pendingShow);
                } else {
                    me.show();
                }
            }
        }
    },

    
    
    
    
    
    setZIndex: function(index) {
        var me = this;

        me.el.setZIndex(index);

        
        index += 10;

        
        
        if (me.floatingDescendants) {
            index = Math.floor(me.floatingDescendants.setBase(index) / 100) * 100 + 10000;
        }
        return index;
    },

    
    doConstrain: function(constrainTo) {
        var me = this,
            
            
            
            xy = me.calculateConstrainedPosition(constrainTo, null, true);

        
        if (xy) {
            me.setPosition(xy);
        }
    },

    
    toFront: function(preventFocus) {
        var me = this,
            zip = me.zIndexParent,
            preventFocusSetting = me.preventFocusOnActivate;

        
        
        if (zip && me.bringParentToFront !== false) {
            zip.toFront(true);
        }

        if (!Ext.isDefined(preventFocus)) {
            preventFocus = !me.focusOnToFront;
        }

        if (preventFocus) {
            me.preventFocusOnActivate = true;
        }
        if (me.zIndexManager.bringToFront(me, preventFocus)) {    
            if (!preventFocus) {
                
                
                
                me.focus(false, true);
            }
        }
        
        
        me.preventFocusOnActivate = preventFocusSetting;
        return me;
    },

    
    setActive: function(active, newActive) {
        var me = this;
        
        if (active) {
            if (me.el.shadow && !me.maximized) {
                me.el.enableShadow(true);
            }
            if (!me.preventFocusOnActivate) {
                me.focus(false, true);
            }
            me.fireEvent('activate', me);
        } else {
            
            
            if (me.isWindow && (newActive && newActive.isWindow) && me.hideShadowOnDeactivate) {
                me.el.disableShadow();
            }
            me.fireEvent('deactivate', me);
        }
    },

    
    toBack: function() {
        this.zIndexManager.sendToBack(this);
        return this;
    },

    
    center: function() {
        var me = this,
            xy;
            
        if (me.isVisible()) {
            xy = me.getAlignToXY(me.container, 'c-c');
            me.setPagePosition(xy);
        } else {
            me.needsCenter = true;
        }
        return me;
    },
    
    onFloatShow: function() {
        if (this.needsCenter) {
            this.center();    
        }
        delete this.needsCenter;

        if (this.toFrontOnShow) {
            this.toFront();
        }
    },

    
    fitContainer: function(animate) {
        var me = this,
            parent = me.floatParent,
            container = parent ? parent.getTargetEl() : me.container,
            newBox = container.getViewSize(false),
            newPosition = parent || (container.dom !== document.body) ?
                
                [0, 0] :
                
                container.getXY();

        newBox.x = newPosition[0];
        newBox.y = newPosition[1];
        me.setBox(newBox, animate);
    }
});


Ext.define('Ext.Component', {

    

    alias: ['widget.component', 'widget.box'],

    extend:  Ext.AbstractComponent ,

           
                               
                    
                              
                                   
      

    mixins: {
        floating:  Ext.util.Floating 
    },

    statics: {
        
        DIRECTION_TOP: 'top',
        DIRECTION_RIGHT: 'right',
        DIRECTION_BOTTOM: 'bottom',
        DIRECTION_LEFT: 'left',

        VERTICAL_DIRECTION_Re: /^(?:top|bottom)$/,

        
        
        INVALID_ID_CHARS_Re: /[\.,\s]/g
    },

    

    

    
    resizeHandles: 'all',

    

    

    

    
    floating: false,
    
    
    defaultAlign: 'tl-bl?',

    
    toFrontOnShow: true,
    
    

    

    

    

    

    
    
    

    

    

    hideMode: 'display',
    
    offsetsCls: Ext.baseCSSPrefix + 'hide-offsets',

    bubbleEvents: [],

    defaultComponentLayoutType: 'autocomponent',
    
    
    
    
    
    
    

    
    constructor: function(config) {
        var me = this;

        config = config || {};
        if (config.initialConfig) {

            
            if (config.isAction) {
                me.baseAction = config;
            }
            config = config.initialConfig;
            
        }
        else if (config.tagName || config.dom || Ext.isString(config)) {
            
            config = {
                applyTo: config,
                id: config.id || config
            };
        }

        me.callParent([config]);

        
        
        if (me.baseAction){
            me.baseAction.addComponent(me);
        }
    },

    
    initComponent: function() {
        var me = this;

        me.callParent();

        if (me.listeners) {
            me.on(me.listeners);
            me.listeners = null; 
        }
        me.enableBubble(me.bubbleEvents);
    },

    afterRender: function() {
        var me = this;

        me.callParent();

        if (!(me.x && me.y) && (me.pageX || me.pageY)) {
            me.setPagePosition(me.pageX, me.pageY);
        }
    },

    
    setAutoScroll : function(scroll) {
        var me = this;

        me.autoScroll = !!scroll;

        
        
        
        if (me.rendered) {
            me.getOverflowEl().setStyle(me.getOverflowStyle());
        }
        me.updateLayout();
        return me;
    },

    
    setOverflowXY: function(overflowX, overflowY) {
        var me = this,
            argCount = arguments.length;

        if (argCount) {
            me.overflowX = overflowX || '';
            if (argCount > 1) {
                me.overflowY = overflowY || '';
            }
        }

        
        
        
        if (me.rendered) {
            me.getOverflowEl().setStyle(me.getOverflowStyle());
        }
        me.updateLayout();
        return me;
    },

    beforeRender: function () {
        var me = this,
            floating = me.floating,
            cls;

        if (floating) {
            me.addCls(Ext.baseCSSPrefix + 'layer');

            cls = floating.cls;
            if (cls) {
                me.addCls(cls);
            }
        }

        return me.callParent();
    },
    
    beforeLayout: function(){
        this.callParent(arguments);
        if (this.floating) {
            this.onBeforeFloatLayout();
        }    
    },
    
    afterComponentLayout: function(){
        this.callParent(arguments);
        if (this.floating) {
            this.onAfterFloatLayout();
        }
    },

    
    makeFloating : function (dom) {
        this.mixins.floating.constructor.call(this, dom);
    },

    wrapPrimaryEl: function (dom) {
        if (this.floating) {
            this.makeFloating(dom);
        } else {
            this.callParent(arguments);
        }
    },

    initResizable: function(resizable) {
        var me = this;

        resizable = Ext.apply({
            target: me,
            dynamic: false,
            constrainTo: me.constrainTo || (me.floatParent ? me.floatParent.getTargetEl() : null),
            handles: me.resizeHandles
        }, resizable);
        resizable.target = me;
        me.resizer = new Ext.resizer.Resizer(resizable);
    },

    getDragEl: function() {
        return this.el;
    },

    initDraggable: function() {
        var me = this,

            
            
            
            dragTarget = (me.resizer && me.resizer.el !== me.el) ? me.resizerComponent = new Ext.Component({
                ariaRole: 'presentation',
                el: me.resizer.el,
                rendered: true,
                container: me.container
            }) : me,
            ddConfig = Ext.applyIf({
                el: dragTarget.getDragEl(),
                constrainTo: (me.constrain||me.draggable.constrain) ? (me.constrainTo || (me.floatParent ? me.floatParent.getTargetEl() : me.container)) : undefined
            }, me.draggable);

        
        if (me.constrain || me.constrainDelegate) {
            ddConfig.constrain = me.constrain;
            ddConfig.constrainDelegate = me.constrainDelegate;
        }

        me.dd = new Ext.util.ComponentDragger(dragTarget, ddConfig);
    },

    
    scrollBy: function(deltaX, deltaY, animate) {
        var el;

        if ((el = this.getTargetEl()) && el.dom) {
            el.scrollBy.apply(el, arguments);
        }
    },

    
    setLoading : function(load, targetEl) {
        var me = this,
            config = {
                target: me
            };

        if (me.rendered) {
            Ext.destroy(me.loadMask);
            me.loadMask = null;

            if (load !== false && !me.collapsed) {
                if (Ext.isObject(load)) {
                    Ext.apply(config, load);
                } else if (Ext.isString(load)) {
                    config.msg = load;
                }
                
                if (targetEl) {
                    Ext.applyIf(config, {
                        useTargetEl: true
                    });
                }
                me.loadMask = new Ext.LoadMask(config);
                me.loadMask.show();
            }
        }
        return me.loadMask;
    },

    beforeSetPosition: function () {
        var me = this,
            pos = me.callParent(arguments), 
            adj;

        if (pos) {
            adj = me.adjustPosition(pos.x, pos.y);
            pos.x = adj.x;
            pos.y = adj.y;
        }
        return pos || null;
    },

    afterSetPosition: function(ax, ay) {
        this.onPosition(ax, ay);
        this.fireEvent('move', this, ax, ay);
    },

    
    showAt: function(x, y, animate) {
        var me = this;

        
        
        if (!me.rendered && (me.autoRender || me.floating)) {
            me.x = x;
            me.y = y;
            return me.show();
        }
        if (me.floating) {
            me.setPosition(x, y, animate);
        } else {
            me.setPagePosition(x, y, animate);
        }
        me.show();
    },
    
    
    showBy: function(cmp, pos, off) {
        var me = this;
        

        if (me.floating && cmp) {
            me.show();

            
            if (me.rendered && !me.hidden) {
                
                
                
                me.alignTo(cmp, pos || me.defaultAlign, off);
            }
        }
        return me;
    },

    
    setPagePosition: function(x, y, animate) {
        var me = this,
            p,
            floatParentBox;

        if (Ext.isArray(x)) {
            y = x[1];
            x = x[0];
        }
        me.pageX = x;
        me.pageY = y;

        if (me.floating) {

            
            if (me.isContainedFloater()) {
                floatParentBox = me.floatParent.getTargetEl().getViewRegion();
                if (Ext.isNumber(x) && Ext.isNumber(floatParentBox.left)) {
                    x -= floatParentBox.left;
                }
                if (Ext.isNumber(y) && Ext.isNumber(floatParentBox.top)) {
                    y -= floatParentBox.top;
                }
            } else {
                p = me.el.translateXY(x, y);
                x = p.x;
                y = p.y;
            }

            me.setPosition(x, y, animate);
        } else {
            p = me.el.translateXY(x, y);
            me.setPosition(p.x, p.y, animate);
        }

        return me;
    },

    
    
    isContainedFloater: function() {
        return (this.floating && this.floatParent);
    },

    
    updateBox : function(box){
        this.setSize(box.width, box.height);
        this.setPagePosition(box.x, box.y);
        return this;
    },

    
    getOuterSize: function() {
        var el = this.el;
        return {
            width: el.getWidth() + el.getMargin('lr'),
            height: el.getHeight() + el.getMargin('tb')
        };
    },

    
    adjustPosition: function(x, y) {
        var me = this,
            floatParentBox;

        
        if (me.isContainedFloater()) {
            floatParentBox = me.floatParent.getTargetEl().getViewRegion();
            x += floatParentBox.left;
            y += floatParentBox.top;
        }

        return {
            x: x,
            y: y
        };
    },

    
    getPosition: function(local) {
        var me = this,
            xy,
            isContainedFloater = me.isContainedFloater(),
            floatParentBox;

        
        if ((local === true) && !isContainedFloater) {
            return [me.getLocalX(), me.getLocalY()];
        }

        xy = me.getXY();

        
        if ((local === true) && isContainedFloater) {
            floatParentBox = me.floatParent.getTargetEl().getViewRegion();
            xy[0] -= floatParentBox.left;
            xy[1] -= floatParentBox.top;
        }
        return xy;
    },

    getId: function() {
        var me = this,
            xtype;

        if (!me.id) {
            xtype = me.getXType();
            if (xtype) {
                xtype = xtype.replace(Ext.Component.INVALID_ID_CHARS_Re, '-');
            } else {
                xtype = Ext.name.toLowerCase() + '-comp';
            }
            me.id = xtype + '-' + me.getAutoId();
        }
        return me.id;
    },

    
    show: function(animateTarget, cb, scope) {
        var me = this,
            rendered = me.rendered;

        if (me.hierarchicallyHidden || (me.floating && !rendered && me.isHierarchicallyHidden())) {
            
            
            
            if (!rendered) {
                
                
                
                
                
                
                
                
                
                me.initHierarchyEvents();
            }
            
            if (arguments.length > 1) {
                arguments[0] = null;
                me.pendingShow = arguments;
            } else {
                me.pendingShow = true;
            }
        } else if (rendered && me.isVisible()) {
            if (me.floating) {
                me.onFloatShow();
            }
        } else {
            if (me.fireEvent('beforeshow', me) !== false) {
                me.hidden = false;
                delete this.getHierarchyState().hidden;
                
                
                
                
                
                
                
                
                
                Ext.suspendLayouts();
                if (!rendered && (me.autoRender || me.floating)) {
                    me.doAutoRender();
                    rendered = me.rendered;
                }
            
                if (rendered) {
                    me.beforeShow();
                    Ext.resumeLayouts();
                    me.onShow.apply(me, arguments);
                    me.afterShow.apply(me, arguments);
                } else {
                    Ext.resumeLayouts(true);
                }
            } else {
                me.onShowVeto();
            }
        }
        return me;
    },
    
    onShowVeto: Ext.emptyFn,

    
    beforeShow: Ext.emptyFn,

    
    onShow: function() {
        var me = this;

        me.el.show();
        me.callParent(arguments);

        
        if (me.floating) {
            if (me.maximized) {
                me.fitContainer();
            }
            else if (me.constrain) {
                me.doConstrain();
            }
        }
    },
    
    getAnimateTarget: function(target){
        target = target || this.animateTarget;
        if (target) {
            target = target.isComponent ? target.getEl() : Ext.get(target);
        }
        return target || null;
    },

    
    afterShow: function(animateTarget, cb, scope) {
        var me = this,
            myEl = me.el,
            fromBox,
            toBox,
            ghostPanel;

        
        animateTarget = me.getAnimateTarget(animateTarget);

        
        if (!me.ghost) {
            animateTarget = null;
        }
        
        if (animateTarget) {
            toBox = {
                x: myEl.getX(),
                y: myEl.getY(),
                width: myEl.dom.offsetWidth,
                height: myEl.dom.offsetHeight
            };
            fromBox = {
                x: animateTarget.getX(),
                y: animateTarget.getY(),
                width: animateTarget.dom.offsetWidth,
                height: animateTarget.dom.offsetHeight
            };
            myEl.addCls(me.offsetsCls);
            ghostPanel = me.ghost();
            ghostPanel.el.stopAnimation();

            
            ghostPanel.setX(-10000);

            me.ghostBox = toBox;
            ghostPanel.el.animate({
                from: fromBox,
                to: toBox,
                listeners: {
                    afteranimate: function() {
                        delete ghostPanel.componentLayout.lastComponentSize;
                        me.unghost();
                        delete me.ghostBox;
                        myEl.removeCls(me.offsetsCls);
                        me.onShowComplete(cb, scope);
                    }
                }
            });
        }
        else {
            me.onShowComplete(cb, scope);
        }
        me.fireHierarchyEvent('show');
    },

    
    onShowComplete: function(cb, scope) {
        var me = this;
        if (me.floating) {
            me.onFloatShow();
        }
        Ext.callback(cb, scope || me);
        me.fireEvent('show', me);
        delete me.hiddenByLayout;
    },

    
    hide: function(animateTarget, cb, scope) {
        var me = this,
            continueHide;

        if (me.pendingShow) {
            
            
            delete me.pendingShow;
        } if (!(me.rendered && !me.isVisible())) {
            continueHide = (me.fireEvent('beforehide', me) !== false);
            if (me.hierarchicallyHidden || continueHide) {
                me.hidden = true;
                me.getHierarchyState().hidden = true;
                if (me.rendered) {
                    me.onHide.apply(me, arguments);
                }
            }
        }
        return me;
    },

    
    onHide: function(animateTarget, cb, scope) {
        var me = this,
            ghostPanel,
            fromSize,
            toBox,
            activeEl = Ext.Element.getActiveElement();

        
        if (activeEl === me.el || me.el.contains(activeEl)) {
            Ext.fly(activeEl).blur();
        }

        
        animateTarget = me.getAnimateTarget(animateTarget);

        
        if (!me.ghost) {
            animateTarget = null;
        }
        
        if (animateTarget) {
            toBox = {
                x: animateTarget.getX(),
                y: animateTarget.getY(),
                width: animateTarget.dom.offsetWidth,
                height: animateTarget.dom.offsetHeight
            };
            ghostPanel = me.ghost();
            ghostPanel.el.stopAnimation();
            fromSize = me.getSize();
            ghostPanel.el.animate({
                to: toBox,
                listeners: {
                    afteranimate: function() {
                        delete ghostPanel.componentLayout.lastComponentSize;
                        ghostPanel.el.hide();
                        ghostPanel.setHiddenState(true);
                        ghostPanel.el.setSize(fromSize);
                        me.afterHide(cb, scope);
                    }
                }
            });
        }
        me.el.hide();
        if (!animateTarget) {
            me.afterHide(cb, scope);
        }
    },

    
    afterHide: function(cb, scope) {
        var me = this;

        me.hiddenByLayout = null;

        
        
        Ext.AbstractComponent.prototype.onHide.call(me);

        Ext.callback(cb, scope || me);
        me.fireEvent('hide', me);
        me.fireHierarchyEvent('hide');
    },

    
    onDestroy: function() {
        var me = this;

        
        if (me.rendered) {
            Ext.destroy(
                me.dd,
                me.resizer,
                me.proxy,
                me.proxyWrap,
                me.resizerComponent
            );
        }
        delete me.focusTask;
        me.callParent();
    },

    deleteMembers: function() {
        var args = arguments,
            len = args.length,
            i = 0;
        for (; i < len; ++i) {
            delete this[args[i]];
        }
    },

    
    focus: function(selectText, delay, callback, scope) {
        var me = this,
            focusEl,
            focusElDom,
            containerScrollTop;

        
        if (delay) {
            me.getFocusTask().delay(Ext.isNumber(delay) ? delay : 10, me.focus, me, [selectText, false, callback, scope]);
            return me;
        }

        
        if (me.focusTask) {
            me.focusTask.cancel();
        }

        if (me.rendered && !me.isDestroyed && me.isVisible(true) && (focusEl = me.getFocusEl())) {

            
            
            if (focusEl.isComponent) {
                return focusEl.focus(selectText, delay);
            }

            
            if ((focusElDom = focusEl.dom)) {

                
                if (focusEl.needsTabIndex()) {
                    focusElDom.tabIndex = -1;
                }

                if (me.floating) {
                    containerScrollTop = me.container.dom.scrollTop;
                }

                
                
                
                focusEl.focus();
                if (selectText) {
                    if (Ext.isArray(selectText)) {
                        if (me.selectText) {
                            me.selectText.apply(me, selectText);
                        }
                    } else {
                        focusElDom.select();
                    }
                }

                
                Ext.callback(callback, scope);
            }

            
            
            if (me.floating) {
                
                
                if (me !== me.zIndexManager.getActive()) {
                    me.toFront(true);
                }

                if (containerScrollTop !== undefined) {
                    me.container.dom.scrollTop = containerScrollTop;
                }
            }
        }
        return me;
    },

    
    getFocusTask: function() {
        if (!this.focusTask) {
            
            
            Ext.Component.prototype.focusTask = new Ext.util.DelayedTask();
        }
        return this.focusTask;
    },

    
    cancelFocus: function() {
        var task = this.focusTask;
        if (task) {
            task.cancel();
        }
    },

    
    blur: function() {
        var me = this,
            focusEl;
            
        if (me.rendered && (focusEl = me.getFocusEl())) {
            me.blurring = true;
            focusEl.blur();
            delete me.blurring;
        }
        return me;
    },

    getEl: function() {
        return this.el;
    },

    
    getResizeEl: function() {
        return this.el;
    },

    
    getPositionEl: function() {
        return this.el;
    },

    
    getActionEl: function() {
        return this.el;
    },

    
    getVisibilityEl: function() {
        return this.el;
    },

    
    getRefOwner: function() {
        return this.ownerCt || this.floatParent;
    },

    
    getBubbleTarget: function() {
        return this.getRefOwner();
    },

    
    getContentTarget: function() {
        return this.el;
    },

    
    cloneConfig: function(overrides) {
        overrides = overrides || {};
        var id = overrides.id || Ext.id(),
            cfg = Ext.applyIf(overrides, this.initialConfig),
            self;

        cfg.id = id;

        self = Ext.getClass(this);

        
        return new self(cfg);
    },

    
    getXType: function() {
        return this.self.xtype;
    },

    
    findParentBy: function(fn) {
        var p;

        
        for (p = this.getBubbleTarget(); p && !fn(p, this); p = p.getBubbleTarget()) {
            
        }
        return p || null;
    },

    
    findParentByType: function(xtype) {
        return Ext.isFunction(xtype) ?
            this.findParentBy(function(p) {
                return p.constructor === xtype;
            })
        :
            this.up(xtype);
    },

    
    bubble: function(fn, scope, args) {
        var p = this;
        while (p) {
            if (fn.apply(scope || p, args || [p]) === false) {
                break;
            }
            p = p.getBubbleTarget();
        }
        return this;
    },

    getProxy: function() {
        var me = this,
            target;

        if (!me.proxy) {
            target = Ext.getBody();
            me.proxy = me.el.createProxy(Ext.baseCSSPrefix + 'proxy-el', target, true);
        }
        return me.proxy;
    },

    
    fireHierarchyEvent: function (ename) {
        this.hierarchyEventSource.fireEvent(ename, this);
    },

    onAdded: function() {
        this.callParent(arguments);
        if (this.hierarchyEventSource.hasListeners.added) {
            this.fireHierarchyEvent('added');
        }
    }
}, function () {
    
    this.hierarchyEventSource = this.prototype.hierarchyEventSource = new Ext.util.Observable({ events: {
        hide: true,
        show: true,
        collapse: true,
        expand: true,
        added: true
    }});
});


Ext.define('Ext.layout.container.border.Region', {
    override: 'Ext.Component',

    
    initBorderRegion: function () {
        var me = this;

        if (!me._borderRegionInited) {
            me._borderRegionInited = true;

            
            
            me.addStateEvents(['changeregion', 'changeweight']);

            
            
            
            
            Ext.override(me, {
                getState: function () {
                    var state = me.callParent();

                    
                    
                    state = me.addPropertyToState(state, 'region');
                    state = me.addPropertyToState(state, 'weight');

                    return state;
                }
            });
        }
    },

    
    getOwningBorderContainer: function () {
        var layout = this.getOwningBorderLayout();
        return layout && layout.owner;
    },

    
    getOwningBorderLayout: function () {
        
        var layout = this.ownerLayout;
        return (layout && layout.isBorderLayout) ? layout : null;
    },

    
    setBorderRegion: function (region) {
        var me = this,
            borderLayout,
            old = me.region;

        if (region !== old) {
            borderLayout = me.getOwningBorderLayout();
            if (borderLayout) {
                var regionFlags = borderLayout.regionFlags[region],
                    placeholder = me.placeholder,
                    splitter = me.splitter,
                    owner = borderLayout.owner,
                    regionMeta = borderLayout.regionMeta,
                    collapsed = me.collapsed || me.floated,
                    delta, items, index;

                if (me.fireEventArgs('beforechangeregion', [me, region]) === false) {
                    return old;
                }
                Ext.suspendLayouts();

                me.region = region;
                Ext.apply(me, regionFlags);

                if (me.updateCollapseTool) {
                    me.updateCollapseTool();
                }

                if (splitter) {
                    
                    Ext.apply(splitter, regionFlags);
                    splitter.updateOrientation();

                    items = owner.items;
                    index = items.indexOf(me);
                    if (index >= 0) {
                        delta = regionMeta[region].splitterDelta;
                        if (items.getAt(index + delta) !== splitter) {
                            
                            items.remove(splitter);
                            index = items.indexOf(me);  
                            if (delta > 0) {
                                ++index;
                            }
                            
                            items.insert(index, splitter);

                            
                            
                            
                        }
                    }
                }
                if (placeholder) {
                    
                    
                    
                    
                    if (collapsed) {
                        me.expand(false);
                    }

                    owner.remove(placeholder);
                    me.placeholder = null; 

                    if (collapsed) {
                        me.collapse(null, false);
                    }
                }

                owner.updateLayout();
                Ext.resumeLayouts(true);

                me.fireEventArgs('changeregion', [me, old]);
            } else {
                me.region = region; 
            }
        }

        return old;
    },

    
    setRegionWeight: function (weight) {
        var me = this,
            ownerCt = me.getOwningBorderContainer(),
            placeholder = me.placeholder,
            old = me.weight;

        if (weight !== old) {
            if (me.fireEventArgs('beforechangeweight', [me, weight]) !== false) {
                me.weight = weight;
                if (placeholder) {
                    placeholder.weight = weight;
                }
                if (ownerCt) {
                    ownerCt.updateLayout();
                }
                me.fireEventArgs('changeweight', [me, old]);
            }
        }

        return old;
    }
});


Ext.define('Ext.ElementLoader', {

    

    mixins: {
        observable:  Ext.util.Observable 
    },

           
                              
                  
      

    statics: {
        Renderer: {
            Html: function(loader, response, active){
                loader.getTarget().update(response.responseText, active.scripts === true);
                return true;
            }
        }
    },

    

    
    url: null,

    
    params: null,

    
    baseParams: null,

    
    autoLoad: false,

    
    target: null,

    
    loadMask: false,

    
    ajaxOptions: null,

    
    scripts: false,

    

    

    

    

    

    
    isLoader: true,

    constructor: function(config) {
        var me = this,
            autoLoad;

        config = config || {};
        Ext.apply(me, config);
        me.setTarget(me.target);
        me.addEvents(
            
            'beforeload',

            
            'exception',

            
            'load'
        );

        
        me.mixins.observable.constructor.call(me);

        if (me.autoLoad) {
            autoLoad = me.autoLoad;
            if (autoLoad === true) {
                autoLoad = {};
            }
            me.load(autoLoad);
        }
    },

    
    setTarget: function(target){
        var me = this;
        target = Ext.get(target);
        if (me.target && me.target != target) {
            me.abort();
        }
        me.target = target;
    },

    
    getTarget: function(){
        return this.target || null;
    },

    
    abort: function(){
        var active = this.active;
        if (active !== undefined) {
            Ext.Ajax.abort(active.request);
            if (active.mask) {
                this.removeMask();
            }
            delete this.active;
        }
    },

    
    removeMask: function(){
        this.target.unmask();
    },

    
    addMask: function(mask){
        this.target.mask(mask === true ? null : mask);
    },

    
    load: function(options) {

        options = Ext.apply({}, options);

        var me = this,
            mask = Ext.isDefined(options.loadMask) ? options.loadMask : me.loadMask,
            params = Ext.apply({}, options.params),
            ajaxOptions = Ext.apply({}, options.ajaxOptions),
            callback = options.callback || me.callback,
            scope = options.scope || me.scope || me;

        Ext.applyIf(ajaxOptions, me.ajaxOptions);
        Ext.applyIf(options, ajaxOptions);

        Ext.applyIf(params, me.params);
        Ext.apply(params, me.baseParams);

        Ext.applyIf(options, {
            url: me.url
        });


        Ext.apply(options, {
            scope: me,
            params: params,
            callback: me.onComplete
        });

        if (me.fireEvent('beforeload', me, options) === false) {
            return;
        }

        if (mask) {
            me.addMask(mask);
        }

        me.active = {
            options: options,
            mask: mask,
            scope: scope,
            callback: callback,
            success: options.success || me.success,
            failure: options.failure || me.failure,
            renderer: options.renderer || me.renderer,
            scripts: Ext.isDefined(options.scripts) ? options.scripts : me.scripts
        };
        me.active.request = Ext.Ajax.request(options);
        me.setOptions(me.active, options);
    },

    
    setOptions: Ext.emptyFn,

    
    onComplete: function(options, success, response) {
        var me = this,
            active = me.active,
            scope;

        if (active) {
            scope = active.scope;
            if (success) {
                success = me.getRenderer(active.renderer).call(me, me, response, active) !== false;
            }

            if (success) {
                Ext.callback(active.success, scope, [me, response, options]);
                me.fireEvent('load', me, response, options);
            } else {
                Ext.callback(active.failure, scope, [me, response, options]);
                me.fireEvent('exception', me, response, options);
            }
            Ext.callback(active.callback, scope, [me, success, response, options]);
            if (active.mask) {
                me.removeMask();
            }
        }

        delete me.active;
    },

    
    getRenderer: function(renderer){
        if (Ext.isFunction(renderer)) {
            return renderer;
        }
        return this.statics().Renderer.Html;
    },

    
    startAutoRefresh: function(interval, options){
        var me = this;
        me.stopAutoRefresh();
        me.autoRefresh = setInterval(function(){
            me.load(options);
        }, interval);
    },

    
    stopAutoRefresh: function(){
        clearInterval(this.autoRefresh);
        delete this.autoRefresh;
    },

    
    isAutoRefreshing: function(){
        return Ext.isDefined(this.autoRefresh);
    },

    
    destroy: function(){
        var me = this;
        me.stopAutoRefresh();
        delete me.target;
        me.abort();
        me.clearListeners();
    }
});


Ext.define('Ext.ComponentLoader', {

    

    extend:  Ext.ElementLoader ,

    statics: {
        Renderer: {
            Data: function(loader, response, active){
                var success = true;
                try {
                    loader.getTarget().update(Ext.decode(response.responseText));
                } catch (e) {
                    success = false;
                }
                return success;
            },

            Component: function(loader, response, active){
                var success = true,
                    target = loader.getTarget(),
                    items = [];


                try {
                    items = Ext.decode(response.responseText);
                } catch (e) {
                    success = false;
                }

                if (success) {
                    target.suspendLayouts();
                    if (active.removeAll) {
                        target.removeAll();
                    }
                    target.add(items);
                    target.resumeLayouts(true);
                }
                return success;
            }
        }
    },

    

    
    target: null,

    
    loadMask: false,

    

    
    renderer: 'html',

    
    setTarget: function(target){
        var me = this;

        if (Ext.isString(target)) {
            target = Ext.getCmp(target);
        }

        if (me.target && me.target != target) {
            me.abort();
        }
        me.target = target;
    },

    
    removeMask: function(){
        this.target.setLoading(false);
    },

    
    addMask: function(mask){
        this.target.setLoading(mask);
    },


    setOptions: function(active, options){
        active.removeAll = Ext.isDefined(options.removeAll) ? options.removeAll : this.removeAll;
    },

    
    getRenderer: function(renderer){
        if (Ext.isFunction(renderer)) {
            return renderer;
        }

        var renderers = this.statics().Renderer;
        switch (renderer) {
            case 'component':
                return renderers.Component;
            case 'data':
                return renderers.Data;
            default:
                return Ext.ElementLoader.Renderer.Html;
        }
    }
});


Ext.define('Ext.layout.SizeModel', {
    constructor: function (config) {
        var me = this,
            SizeModel = me.self,
            sizeModelsArray = SizeModel.sizeModelsArray,
            name;

        Ext.apply(me, config);

        me[name = me.name] = true; 

        me.fixed = !(me.auto = me.natural || me.shrinkWrap);

        
        sizeModelsArray[me.ordinal = sizeModelsArray.length] =
            SizeModel[name] =
            SizeModel.sizeModels[name] = me;
    },

    statics: {
        
        sizeModelsArray: [],

        
        sizeModels: {}
    },

    

    

    
    calculated: false,

    
    configured: false,

    
    constrainedMax: false,

    
    constrainedMin: false,

    

    
    natural: false,

    
    shrinkWrap: false,

    
    calculatedFromConfigured: false,

    
    calculatedFromNatural: false,

    
    calculatedFromShrinkWrap: false,

    
    names: null
},
function () {
    var SizeModel = this,
        sizeModelsArray = SizeModel.sizeModelsArray,
        i, j, n, pairs, sizeModel;

    
    

    new SizeModel({
        name: 'calculated'
    });

    new SizeModel({
        name: 'configured',
        names: { width: 'width', height: 'height' }
    });

    new SizeModel({
        name: 'natural'
    });

    new SizeModel({
        name: 'shrinkWrap'
    });

    
    
    

    new SizeModel({
        name: 'calculatedFromConfigured',
        configured: true,
        names: { width: 'width', height: 'height' }
    });

    new SizeModel({
        name: 'calculatedFromNatural',
        natural: true
    });

    new SizeModel({
        name: 'calculatedFromShrinkWrap',
        shrinkWrap: true
    });

    new SizeModel({
        name: 'constrainedMax',
        configured: true,
        constrained: true,
        names: { width: 'maxWidth', height: 'maxHeight' }
    });

    new SizeModel({
        name: 'constrainedMin',
        configured: true,
        constrained: true,
        names: { width: 'minWidth', height: 'minHeight' }
    });

    new SizeModel({
        name: 'constrainedDock',
        configured: true,
        constrained: true,
        constrainedByMin: true,
        names: { width: 'dockConstrainedWidth', height: 'dockConstrainedHeight' }
    });

    for (i = 0, n = sizeModelsArray.length; i < n; ++i) {
        sizeModel = sizeModelsArray[i];

        
        sizeModel.pairsByHeightOrdinal = pairs = [];

        for (j = 0; j < n; ++j) {
            pairs.push({
                width: sizeModel,
                height: sizeModelsArray[j]
            });
        }
    }
});


Ext.define('Ext.layout.Layout', {
               
                        
                              
      

                                   

    
    isLayout: true,
    initialized: false,
    running: false,

    autoSizePolicy: {
        readsWidth: 1,
        readsHeight: 1,
        setsWidth: 0,
        setsHeight: 0
    },

    statics: {
        layoutsByType: {},

        create: function(layout, defaultType) {
            var ClassManager = Ext.ClassManager,
                layoutsByType = this.layoutsByType,
                alias, className, config, layoutClass, type, load;

            if (!layout || typeof layout === 'string') {
                type = layout || defaultType;
                config = {};                    
            } else if (layout.isLayout) {
                return layout;
            } else {
                config = layout;
                type = layout.type || defaultType;
            }

            if (!(layoutClass = layoutsByType[type])) {
                alias = 'layout.' + type;
                className = ClassManager.getNameByAlias(alias);

                
                if (!className) {
                    load = true;
                }
                
                layoutClass = ClassManager.get(className);
                if (load || !layoutClass) {
                    return ClassManager.instantiateByAlias(alias, config || {});
                }
                layoutsByType[type] = layoutClass;
            }

            return new layoutClass(config);
        }
    },

    constructor : function(config) {
        var me = this;

        me.id = Ext.id(null, me.type + '-');
        Ext.apply(me, config);
        me.layoutCount = 0;
    },

    

    
    beginLayout: Ext.emptyFn,

    
    beginLayoutCycle: function (ownerContext) {
        var me = this,
            context = me.context,
            changed;

        if (me.lastWidthModel != ownerContext.widthModel) {
            if (me.lastWidthModel) {
                changed = true;
            }
            me.lastWidthModel = ownerContext.widthModel;
        }

        if (me.lastHeightModel != ownerContext.heightModel) {
            if (me.lastWidthModel) {
                changed = true;
            }
            me.lastHeightModel = ownerContext.heightModel;
        }

        if (changed) {
            (context = ownerContext.context).clearTriggers(me, false);
            context.clearTriggers(me, true);
            me.triggerCount = 0;
        }
    },

    

    

    

    
    finishedLayout: function (ownerContext) {
        this.lastWidthModel = ownerContext.widthModel;
        this.lastHeightModel = ownerContext.heightModel;
        this.ownerContext = null;
    },
    
    
    
    redoLayout: Ext.emptyFn,
    undoLayout: Ext.emptyFn,

    getAnimatePolicy: function() {
        return this.animatePolicy;
    },

    
    getItemSizePolicy: function (item) {
        return this.autoSizePolicy;
    },

    isItemBoxParent: function (itemContext) {
        return false;
    },

    isItemLayoutRoot: function (item) {
        var sizeModel = item.getSizeModel(),
            width = sizeModel.width,
            height = sizeModel.height;

        
        
        if (!item.componentLayout.lastComponentSize && (width.calculated || height.calculated)) {
            return false;
        }

        
        return !width.shrinkWrap && !height.shrinkWrap;
    },

    isItemShrinkWrap: function (item) {
        return item.shrinkWrap;
    },

    isRunning: function () {
        return !!this.ownerContext;
    },

    
    
    

    getItemsRenderTree: function (items, renderCfgs) {
        var length = items.length,
            i, item, itemConfig, result;

        if (length) {
            result = [];
            for (i = 0; i < length; ++i) {
                item = items[i];

                
                
                if (!item.rendered) {

                    
                    
                    
                    
                    
                    if (renderCfgs && (renderCfgs[item.id] !== undefined)) {
                        itemConfig = renderCfgs[item.id];
                    } else {
                        
                        this.configureItem(item);
                        itemConfig = item.getRenderTree();
                        if (renderCfgs) {
                            renderCfgs[item.id] = itemConfig;
                        }
                    }

                    
                    if (itemConfig) {
                        result.push(itemConfig);
                    }
                }
            }
        }

        return result;
    },

    finishRender: Ext.emptyFn,

    finishRenderItems: function (target, items) {
        var length = items.length,
            i, item;

        for (i = 0; i < length; i++) {
            item = items[i];

            
            if (item.rendering) {

                
                item.finishRender(i);

                this.afterRenderItem(item);
            }
        }
    },

    renderChildren: function () {
        var me = this,
            items = me.getLayoutItems(),
            target = me.getRenderTarget();

        me.renderItems(items, target);
    },

    
    renderItems : function(items, target) {
        var me = this,
            ln = items.length,
            i = 0,
            item;

        if (ln) {
            Ext.suspendLayouts();
            for (; i < ln; i++) {
                item = items[i];
                if (item && !item.rendered) {
                    me.renderItem(item, target, i);
                } else if (!me.isValidParent(item, target, i)) {
                    me.moveItem(item, target, i);
                } else {
                    
                    me.configureItem(item);
                }
            }
            Ext.resumeLayouts(true);
        }
    },

    
    isValidParent : function(item, target, position) {
        var itemDom = item.el ? item.el.dom : Ext.getDom(item),
            targetDom = (target && target.dom) || target,
            parentNode = itemDom.parentNode,
            className;

        
        if (parentNode) {
            className = parentNode.className;
            if (className && className.indexOf(Ext.baseCSSPrefix + 'resizable-wrap') !== -1) {
                itemDom = itemDom.parentNode;
            }
        }

        
        if (itemDom && targetDom) {
            if (typeof position == 'number') {
                position = this.getPositionOffset(position);
                return itemDom === targetDom.childNodes[position];
            }
            return itemDom.parentNode === targetDom;
        }

        return false;
    },
    
    getPositionOffset: function(position){
        return position;
    },

    
    configureItem: function(item) {
        item.ownerLayout = this;
    },

    
    renderItem : function(item, target, position) {
        var me = this;
        if (!item.rendered) {
            me.configureItem(item);
            item.render(target, position);
            me.afterRenderItem(item);
        }
    },

    
    moveItem : function(item, target, position) {
        target = target.dom || target;
        if (typeof position == 'number') {
            position = target.childNodes[position];
        }
        target.insertBefore(item.el.dom, position || null);
        item.container = Ext.get(target);
        this.configureItem(item);
    },

    
    onContentChange: function () {
        this.owner.updateLayout();
        return true;
    },

    
    initLayout : function() {
        this.initialized = true;
    },

    
    setOwner : function(owner) {
        this.owner = owner;
    },

    
    getLayoutItems : function() {
        return [];
    },

    onAdd: function (item) {
        item.ownerLayout = this;
    },
    afterRenderItem: Ext.emptyFn,
    onRemove : Ext.emptyFn,
    onDestroy : Ext.emptyFn,

    
    afterRemove : function(item) {
        var me = this,
            el = item.el,
            owner = me.owner,
            removeClasses;

        if (item.rendered) {
            removeClasses = [].concat(me.itemCls || []);
            if (owner.itemCls) {
                removeClasses = Ext.Array.push(removeClasses, owner.itemCls);
            }
            if (removeClasses.length) {
                el.removeCls(removeClasses);
            }
        }

        delete item.ownerLayout;
    },

    
    destroy : function() {
        var me = this,
            target;

        if (me.targetCls) {
            target = me.getTarget();
            if (target) {
                target.removeCls(me.targetCls);
            }
        }

        me.onDestroy();
    },

    sortWeightedItems: function (items, reverseProp) {
        for (var i = 0, length = items.length; i < length; ++i) {
            items[i].$i = i;
        }

        Ext.Array.sort(items, function (item1, item2) {
            var ret = item2.weight - item1.weight;

            if (!ret) {
                ret = item1.$i - item2.$i;
                if (item1[reverseProp]) {
                    ret = -ret;
                }
            }

            return ret;
        });

        for (i = 0; i < length; ++i) {
            delete items[i].$i;
        }
    }
}, function () {
    var Layout = this;

    Layout.prototype.sizeModels = Layout.sizeModels = Ext.layout.SizeModel.sizeModels;
});


Ext.define('Ext.layout.container.Container', {

    

    alias: ['layout.container'],

    extend:  Ext.layout.Layout ,

    alternateClassName: 'Ext.layout.ContainerLayout',

    mixins: {
        elementCt:  Ext.util.ElementContainer 
    },

               
                       
      

    type: 'container',

    

    

    
    beginCollapse: Ext.emptyFn,

    
    beginExpand: Ext.emptyFn,

    
    animatePolicy: null,

    childEls: [
        
        'overflowPadderEl'
    ],

    renderTpl: [
        '{%this.renderBody(out,values)%}'
    ],

    usesContainerHeight: true,
    usesContainerWidth: true,
    usesHeight: true,
    usesWidth: true,

    constructor: function () {
        this.callParent(arguments);
        this.mixins.elementCt.constructor.call(this);
    },

    destroy : function() {
        this.callParent();
        this.mixins.elementCt.destroy.call(this);
    },

    
    beginLayout: function (ownerContext) {
        this.callParent(arguments);

        ownerContext.targetContext = ownerContext.paddingContext = ownerContext.getEl('getTarget', this);

        this.cacheChildItems(ownerContext);
    },

    beginLayoutCycle: function (ownerContext, firstCycle) {
        var me = this;

        me.callParent(arguments);

        if (firstCycle) {
            if (me.usesContainerHeight) {
                ++ownerContext.consumersContainerHeight;
            }
            if (me.usesContainerWidth) {
                ++ownerContext.consumersContainerWidth;
            }
        }
    },

    cacheChildItems: function (ownerContext) {
        var context = ownerContext.context,
            childItems = [],
            items = this.getVisibleItems(),
            length = items.length,
            i;

        ownerContext.childItems = childItems;
        ownerContext.visibleItems = items;

        for (i = 0; i < length; ++i) {
            childItems.push(context.getCmp(items[i]));
        }
    },

    cacheElements: function () {
        var owner = this.owner;

        this.applyChildEls(owner.el, owner.id); 
    },

    
    configureItem: function(item) {
        var me = this,
            itemCls = me.itemCls,
            ownerItemCls = me.owner.itemCls,
            addClasses;

        
        item.ownerLayout = me;

        if (itemCls) {
            
            addClasses = typeof itemCls === 'string' ? [itemCls] : itemCls;
        }
        if (ownerItemCls) {
            addClasses = Ext.Array.push(addClasses||[], ownerItemCls);
        }
        if (addClasses) {
            item.addCls(addClasses);
        }
    },

    doRenderBody: function (out, renderData) {
        
        

        this.renderItems(out, renderData);
        this.renderContent(out, renderData);
    },

    doRenderContainer: function (out, renderData) {
        
        

        var me = renderData.$comp.layout,
            tpl = me.getRenderTpl(),
            data = me.getRenderData();

        tpl.applyOut(data, out);
    },

    doRenderItems: function (out, renderData) {
        
        

        var me = renderData.$layout,
            tree = me.getRenderTree();

        if (tree) {
            Ext.DomHelper.generateMarkup(tree, out);
        }
    },

    finishRender: function () {
        var me = this,
            target, items;

        me.callParent();

        me.cacheElements();

        target = me.getRenderTarget();
        items = me.getLayoutItems();


        me.finishRenderItems(target, items);
    },

    
    notifyOwner: function() {
        this.owner.afterLayout(this);
    },

    
    getContainerSize : function(ownerContext, inDom) {
        
        
        
        
        

        var targetContext = ownerContext.targetContext,
            frameInfo = targetContext.getFrameInfo(),
            padding = ownerContext.paddingContext.getPaddingInfo(),
            got = 0,
            needed = 0,
            gotWidth, gotHeight, width, height;

        
        
        

        
        
        

        if (!ownerContext.widthModel.shrinkWrap) {
            ++needed;
            width = inDom ? targetContext.getDomProp('width') : targetContext.getProp('width');
            gotWidth = (typeof width == 'number');
            if (gotWidth) {
                ++got;
                width -= frameInfo.width + padding.width;
                if (width < 0) {
                    width = 0;
                }
            }
        }

        if (!ownerContext.heightModel.shrinkWrap) {
            ++needed;
            height = inDom ? targetContext.getDomProp('height') : targetContext.getProp('height');
            gotHeight = (typeof height == 'number');
            if (gotHeight) {
                ++got;
                height -= frameInfo.height + padding.height;
                if (height < 0) {
                    height = 0;
                }
            }
        }

        return {
            width: width,
            height: height,
            needed: needed,
            got: got,
            gotAll: got == needed,
            gotWidth: gotWidth,
            gotHeight: gotHeight
        };
    },
    
    
    
    
    
    
    
    
    
    getPositionOffset: function(position) {
        if (!this.createsInnerCt) {
            var offset = this.owner.itemNodeOffset;
            if (offset) {
                position += offset;
            }
        }
        return position;
    },

    
    getLayoutItems: function() {
        var owner = this.owner,
            items = owner && owner.items;

        return (items && items.items) || [];
    },

    getRenderData: function () {
        var comp = this.owner;

        return {
            $comp: comp,
            $layout: this,
            ownerId: comp.id
        };
    },

    
    getRenderedItems: function() {
        var me = this,
            target = me.getRenderTarget(),
            items = me.getLayoutItems(),
            ln = items.length,
            renderedItems = [],
            i, item;

        for (i = 0; i < ln; i++) {
            item = items[i];
            if (item.rendered && me.isValidParent(item, target, i)) {
                renderedItems.push(item);
            }
        }

        return renderedItems;
    },

    
    getRenderTarget: function() {
        return this.owner.getTargetEl();
    },

    
    getElementTarget: function() {
        return this.getRenderTarget();
    },

    getRenderTpl: function () {
        var me = this,
            renderTpl = Ext.XTemplate.getTpl(this, 'renderTpl');

        
        
        if (!renderTpl.renderContent) {
            me.owner.setupRenderTpl(renderTpl);
        }

        return renderTpl;
    },

    getRenderTree: function () {
        var result,
            items = this.owner.items,
            itemsGen,
            renderCfgs = {};
        
        do {
            itemsGen = items.generation;
            result = this.getItemsRenderTree(this.getLayoutItems(), renderCfgs);
        } while (items.generation !== itemsGen);
        return result;
    },
    
    renderChildren: function () {
        var me = this,
            ownerItems = me.owner.items,
            target = me.getRenderTarget(),
            itemsGen, items;
            
        
        
        
        do {
            itemsGen = ownerItems.generation;
            items = me.getLayoutItems();
            me.renderItems(items, target);
        } while (ownerItems.generation !== itemsGen);
    },

    getScrollbarsNeeded: function (width, height, contentWidth, contentHeight) {
        var scrollbarSize = Ext.getScrollbarSize(),
            hasWidth = typeof width == 'number',
            hasHeight = typeof height == 'number',
            needHorz = 0,
            needVert = 0;

        
        if (!scrollbarSize.width) {
            return 0;
        }
        if (hasHeight && height < contentHeight) {
            needVert = 2;
            width -= scrollbarSize.width;
        }

        if (hasWidth && width < contentWidth) {
            needHorz = 1;
            if (!needVert && hasHeight) {
                height -= scrollbarSize.height;
                if (height < contentHeight) {
                    needVert = 2;
                }
            }
        }

        return needVert + needHorz;
    },

    
    getTarget: function() {
        return this.owner.getTargetEl();
    },

    
    getVisibleItems: function() {
        var target   = this.getRenderTarget(),
            items = this.getLayoutItems(),
            ln = items.length,
            visibleItems = [],
            i, item;

        for (i = 0; i < ln; i++) {
            item = items[i];
            if (item.rendered && this.isValidParent(item, target, i) && item.hidden !== true) {
                visibleItems.push(item);
            }
        }

        return visibleItems;
    },

    setupRenderTpl: function (renderTpl) {
        var me = this;

        renderTpl.renderBody = me.doRenderBody;
        renderTpl.renderContainer = me.doRenderContainer;
        renderTpl.renderItems = me.doRenderItems;
    },
    
    getContentTarget: function(){
        return this.owner.getDefaultContentTarget();
    }

});


Ext.define('Ext.layout.container.Auto', {

    

    alias: ['layout.auto', 'layout.autocontainer'],

    extend:  Ext.layout.container.Container ,

    

    type: 'autocontainer',

    childEls: [
        'outerCt',
        'innerCt',
        'clearEl'
    ],

    
    reserveScrollbar: false,

    
    managePadding: true,

    
    manageOverflow: false,

    
    lastOverflowAdjust: {
        width: 0,
        height: 0
    },

    
    
    
    
    
    
    
    
    
    
    
    
    renderTpl: [
        '{% if (!(Ext.isIEQuirks || Ext.isIE7m)) { %}',
            
            
            
            
            
            
            
            '<span id="{ownerId}-outerCt" style="display:table;" role="presentation">',
                
                
                '<div id="{ownerId}-innerCt" style="display:table-cell;height:100%;',
                'vertical-align:top;{%this.renderPadding(out, values)%}" class="{innerCtCls}" role="presentation">',
                    '{%this.renderBody(out,values)%}',
                '</div>',
            '</span>',
        '{% } else if (values.shrinkWrapWidth) { %}',
            
            
            
            '<table id="{ownerId}-outerCt" class="' + Ext.plainTableCls + '" role="presentation">',
                '<tbody role="presentation">',
                    '<tr role="presentation">',
                        '<td id="{ownerId}-innerCt" style="vertical-align:top;padding:0;',
                            '{%this.renderPadding(out, values)%}" class="{innerCtCls}" role="presentation">',
                            '{%this.renderBody(out,values)%}',
                             
                            '<div id="{ownerId}-clearEl" class="', Ext.baseCSSPrefix,  'clear"',
                                'role="presentation"></div>',
                        '</td>',
                    '</tr>',
                '</tbody>',
            '</table>',
        '{% } else { %}',
            
            
            
            
            
            
            '<div id="{ownerId}-outerCt" style="zoom:1;{%this.renderPadding(out, values)%}" role="presentation">',
                '<div id="{ownerId}-innerCt" style="zoom:1;height:100%;" class="{innerCtCls}" role="presentation">',
                    '{%this.renderBody(out,values)%}',
                     
                    '<div id="{ownerId}-clearEl" class="', Ext.baseCSSPrefix,  'clear"',
                        'role="presentation"></div>',
                '</div>',
            '</div>',
            
            '{% values.$layout.isShrinkWrapTpl = false %}',
        '{% } %}'
    ],

    
    
    
    
    tableTpl: [
        '<table id="{ownerId}-outerCt" class="' + Ext.plainTableCls + '" role="presentation">',
            '<tbody role="presentation">',
                '<tr role="presentation">',
                    '<td id="{ownerId}-innerCt" style="vertical-align:top;padding:0;',
                        '{%this.renderPadding(out, values)%}" class="{innerCtCls}" role="presentation">',
                    '</td>',
                '</tr>',
            '</tbody>',
        '</table>'
    ],

    isShrinkWrapTpl: true,


    beginLayout: function(ownerContext) {
        var me = this,
            bottomPadding, overflowYStyle, overflowXStyle, needsTable;
        
        me.callParent(arguments);

        me.initContextItems(ownerContext);

        if (!me.isShrinkWrapTpl) {
            
            
            
            
            if (ownerContext.widthModel.shrinkWrap) {
                needsTable = true;
            }

            
            
            if (Ext.isStrict && Ext.isIE7) {
                overflowXStyle = me.getOverflowXStyle(ownerContext);
                if ((overflowXStyle === 'auto' || overflowXStyle === 'scroll') &&
                    ownerContext.paddingContext.getPaddingInfo().right) {
                    needsTable = true;
                }
            }

            if (needsTable) {
                me.insertTableCt(ownerContext);
            }
        }

        
        
        
        if (!me.isShrinkWrapTpl && Ext.isIE7 && Ext.isStrict && !me.clearElHasPadding) {
             bottomPadding = ownerContext.paddingContext.getPaddingInfo().bottom;
             overflowYStyle = me.getOverflowYStyle(ownerContext);
             if (bottomPadding && (overflowYStyle === 'auto' || overflowYStyle === 'scroll')) {
                 me.clearEl.setStyle('height', bottomPadding);
                 me.clearElHasPadding = true;
             }
        }
    },
    
    beforeLayoutCycle: function(ownerContext){
        var comp = this.owner,
            hierarchyState = comp.hierarchyState,
            hierarchyStateInner = comp.hierarchyStateInner;

        if (!hierarchyState || hierarchyState.invalid) {
            hierarchyState = comp.getHierarchyState(); 
            hierarchyStateInner = comp.hierarchyStateInner;
        }
        if (ownerContext.widthModel.shrinkWrap && this.isShrinkWrapTpl) {
            hierarchyStateInner.inShrinkWrapTable = true;
        } else {
            delete hierarchyStateInner.inShrinkWrapTable;
        }
    },

    beginLayoutCycle: function(ownerContext) {
        var me = this,
            outerCt = me.outerCt,
            lastOuterCtWidth = me.lastOuterCtWidth || '',
            lastOuterCtHeight = me.lastOuterCtHeight || '',
            lastOuterCtTableLayout = me.lastOuterCtTableLayout || '',
            state = ownerContext.state,
            overflowXStyle, overflowYStyle, outerCtWidth, outerCtHeight, outerCtTableLayout,
            deferWidth, hierarchyStateInner;

        me.callParent(arguments);

        
        outerCtWidth = outerCtHeight = outerCtTableLayout = '';

        if (!ownerContext.widthModel.shrinkWrap && me.isShrinkWrapTpl) {
            
            
            
            if (Ext.isIE7m && Ext.isStrict) {
                overflowYStyle = me.getOverflowYStyle(ownerContext);
                if (overflowYStyle === 'auto' || overflowYStyle === 'scroll') {
                    
                    
                    
                    deferWidth = true;
                }
            }

            if (!deferWidth) {
                
                outerCtWidth = '100%';
            }
            hierarchyStateInner = me.owner.hierarchyStateInner;
            
            
            
            overflowXStyle = me.getOverflowXStyle(ownerContext);
            outerCtTableLayout = (hierarchyStateInner.inShrinkWrapTable ||
                                  overflowXStyle === 'auto' || 
                                  overflowXStyle === 'scroll') ? '' : 'fixed';
        }

        if (!ownerContext.heightModel.shrinkWrap && 
            !Ext.supports.PercentageHeightOverflowBug) {
            
            
            
            
            
            outerCtHeight = '100%';
        }

        
        
        
        if ((outerCtWidth !== lastOuterCtWidth) || me.hasOuterCtPxWidth) {
            outerCt.setStyle('width', outerCtWidth);
            me.lastOuterCtWidth = outerCtWidth;
            me.hasOuterCtPxWidth = false;
        }

        
        if (outerCtTableLayout !== lastOuterCtTableLayout) {
            outerCt.setStyle('table-layout', outerCtTableLayout);
            me.lastOuterCtTableLayout = outerCtTableLayout;
        }

        
        
        
        if ((outerCtHeight !== lastOuterCtHeight) || me.hasOuterCtPxHeight) {
            outerCt.setStyle('height', outerCtHeight);
            me.lastOuterCtHeight = outerCtHeight;
            me.hasOuterCtPxHeight = false;
        }

        if (me.hasInnerCtPxHeight) {
            me.innerCt.setStyle('height', '');
            me.hasInnerCtPxHeight = false;
        }

        
        
        
        state.overflowAdjust = state.overflowAdjust || me.lastOverflowAdjust;
    },

    calculate: function(ownerContext) {
        var me = this,
            state = ownerContext.state,
            containerSize = me.getContainerSize(ownerContext, true),
            
            calculatedItems = state.calculatedItems ||
                (state.calculatedItems = me.calculateItems ?
                me.calculateItems(ownerContext, containerSize) : true);

        me.setCtSizeIfNeeded(ownerContext, containerSize);

        if (calculatedItems && ownerContext.hasDomProp('containerChildrenSizeDone')) {

            me.calculateContentSize(ownerContext);

            if (containerSize.gotAll) {
                if (me.manageOverflow && !ownerContext.state.secondPass && !me.reserveScrollbar) {
                    me.calculateOverflow(ownerContext, containerSize);
                }
                return;
            }
        }
        
        me.done = false;
    },

    calculateContentSize: function (ownerContext) {
        var me = this,
            containerDimensions = ((ownerContext.widthModel.shrinkWrap ? 1 : 0) |
                                   (ownerContext.heightModel.shrinkWrap ? 2 : 0)),
            calcWidth = (containerDimensions & 1) || undefined,
            calcHeight = (containerDimensions & 2) || undefined,
            needed = 0,
            props = ownerContext.props;

        if (calcWidth) {
            if (isNaN(props.contentWidth)) {
                ++needed;
            } else {
                calcWidth = undefined;
            }
        }
        if (calcHeight) {
            if (isNaN(props.contentHeight)) {
                ++needed;
            } else {
                calcHeight = undefined;
            }
        }

        if (needed) {
            if (calcWidth && !ownerContext.setContentWidth(me.measureContentWidth(ownerContext))) {
                me.done = false;
            }
            if (calcHeight && !ownerContext.setContentHeight(me.measureContentHeight(ownerContext))) {
                me.done = false;
            }

            
            
            
            
            
        }
    },

    
    calculateOverflow: function (ownerContext) {
        var me = this,
            width, height, scrollbarSize, scrollbars, xauto, yauto, targetEl;

        
        
        xauto = (me.getOverflowXStyle(ownerContext) === 'auto');
        yauto = (me.getOverflowYStyle(ownerContext) === 'auto');

        if (xauto || yauto) {
            scrollbarSize = Ext.getScrollbarSize();
            targetEl = ownerContext.overflowContext.el.dom;
            scrollbars = 0;

            if (targetEl.scrollWidth > targetEl.clientWidth) {
                
                scrollbars |= 1;
            }

            if (targetEl.scrollHeight > targetEl.clientHeight) {
                
                scrollbars |= 2;
            }

            width = (yauto && (scrollbars & 2)) ? scrollbarSize.width : 0;
            height = (xauto && (scrollbars & 1)) ? scrollbarSize.height : 0;

            if (width !== me.lastOverflowAdjust.width || height !== me.lastOverflowAdjust.height) {
                me.done = false;

                
                
                ownerContext.invalidate({
                    state: {
                        overflowAdjust: {
                            width: width,
                            height: height
                        },
                        overflowState: scrollbars,
                        secondPass: true
                    }
                });
            }
        }
    },

    completeLayout: function(ownerContext) {
       this.lastOverflowAdjust = ownerContext.state.overflowAdjust;
    },

    doRenderPadding: function(out, renderData) {
        
        

        var me = renderData.$layout,
            owner = renderData.$layout.owner,
            padding = owner[owner.contentPaddingProperty];

        if (me.managePadding && padding) {
            out.push('padding:', owner.unitizeBox(padding));
        }
    },

    finishedLayout: function (ownerContext) {
        var innerCt = this.innerCt;

        this.callParent(arguments);

        if (Ext.isIEQuirks || Ext.isIE8m)  {
            
            
            
            innerCt.repaint();
        }

        if (Ext.isOpera) {
            
            
            
            innerCt.setStyle('position', 'relative');
            innerCt.dom.scrollWidth;
            innerCt.setStyle('position', '');
        }
    },

    
    getContainerSize : function(ownerContext, inDom) {
        
        
        
        
        
        
        var size = this.callParent(arguments),
            overflowAdjust = ownerContext.state.overflowAdjust;

        if (overflowAdjust) {
            size.width -= overflowAdjust.width;
            size.height -= overflowAdjust.height;
        }

        return size;
    },

    getRenderData: function() {
        var owner = this.owner,
            data = this.callParent();
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        if ((Ext.isIEQuirks || Ext.isIE7m) && 
            ((owner.shrinkWrap & 1) || 
            (owner.floating && !owner.width))) { 
            data.shrinkWrapWidth = true;
        }

        return data;
    },

    
    
    getRenderTarget: function() {
        return this.innerCt;
    },

    
    
    getElementTarget: function() {
        return this.innerCt;
    },

    
    getOverflowXStyle: function(ownerContext) {
        return ownerContext.overflowXStyle ||
            (ownerContext.overflowXStyle = this.owner.scrollFlags.overflowX || ownerContext.overflowContext.getStyle('overflow-x'));
    },

    
    getOverflowYStyle: function(ownerContext) {
        return ownerContext.overflowYStyle || 
            (ownerContext.overflowYStyle = this.owner.scrollFlags.overflowY || ownerContext.overflowContext.getStyle('overflow-y'));
    },

    initContextItems: function(ownerContext) {
        var me = this,
            target = ownerContext.target,
            customOverflowEl = me.owner.customOverflowEl;

        ownerContext.outerCtContext = ownerContext.getEl('outerCt', me);
        ownerContext.innerCtContext = ownerContext.getEl('innerCt', me);
        
        if (customOverflowEl) {
            ownerContext.overflowContext = ownerContext.getEl(customOverflowEl);    
        } else {
            ownerContext.overflowContext = ownerContext.targetContext;
        }
        
        if (target[target.contentPaddingProperty] !== undefined) {
            
            
            
            
            
            ownerContext.paddingContext = me.isShrinkWrapTpl ?
                ownerContext.innerCtContext : ownerContext.outerCtContext;
        }
    },

    initLayout: function() {
        var me = this,
            scrollbarWidth = Ext.getScrollbarSize().width,
            owner = me.owner;

        me.callParent();

        
        
        
        if (scrollbarWidth && me.manageOverflow && !me.hasOwnProperty('lastOverflowAdjust')) {
            if (owner.autoScroll || me.reserveScrollbar) {
                me.lastOverflowAdjust = {
                    width: scrollbarWidth,
                    height: 0
                };
            }
        }
    },

    
    insertTableCt: function(ownerContext) {
        var me = this,
            owner = me.owner,
            i = 0,
            renderTpl, fragment, childNodes, childLength, targetEl;

        
        renderTpl = Ext.XTemplate.getTpl(this, 'tableTpl');
        renderTpl.renderPadding = me.doRenderPadding;

        
        
        me.outerCt.dom.removeChild(me.innerCt.dom);
        
        
        fragment = document.createDocumentFragment();
        childNodes = me.innerCt.dom.childNodes;
        childLength = childNodes.length;
        
        for (; i < childLength; i++) {
            fragment.appendChild(childNodes[0]);
        }
        
        targetEl = me.getTarget();
        targetEl.dom.innerHTML = renderTpl.apply({
            $layout: me,
            ownerId: me.owner.id
        });

        
        targetEl.down('td').dom.appendChild(fragment);
        
        
        
        me.applyChildEls(owner.el, owner.id)
        
        
        
        me.isShrinkWrapTpl = true;
        
        ownerContext.removeEl(me.outerCt);
        ownerContext.removeEl(me.innerCt);
        me.initContextItems(ownerContext);
    },

    measureContentHeight: function (ownerContext) {
        
        var contentHeight = this.outerCt.getHeight(),
            target = ownerContext.target;

        if (this.managePadding && (target[target.contentPaddingProperty] === undefined)) {
            
            
            
            
            contentHeight += ownerContext.targetContext.getPaddingInfo().height;
        }
        return contentHeight;
    },

    measureContentWidth: function (ownerContext) {
        var dom, style, old, contentWidth, target;
            
        
        
        
        if (this.chromeCellMeasureBug) {
            dom = this.innerCt.dom;
            style = dom.style;
            old = style.display;
            
            if (old == 'table-cell') {
                style.display = '';
                dom.offsetWidth;
                style.display = old;
            }    
        }
        
        
        contentWidth = this.outerCt.getWidth();
        target = ownerContext.target;

        if (this.managePadding && (target[target.contentPaddingProperty] === undefined)) {
            
            
            
            
            contentWidth += ownerContext.targetContext.getPaddingInfo().width;
        }
        return contentWidth;
    },

    
    setCtSizeIfNeeded: function(ownerContext, containerSize) {
        var me = this,
            width = containerSize.width,
            height = containerSize.height,
            padding = ownerContext.paddingContext.getPaddingInfo(),
            targetEl = me.getTarget(),
            overflowXStyle = me.getOverflowXStyle(ownerContext),
            overflowYStyle = me.getOverflowYStyle(ownerContext),
            canOverflowX = (overflowXStyle === 'auto' || overflowXStyle === 'scroll'),
            canOverflowY = (overflowYStyle === 'auto' || overflowYStyle === 'scroll'),
            scrollbarSize = Ext.getScrollbarSize(),
            isShrinkWrapTpl = me.isShrinkWrapTpl,
            manageOverflow = me.manageOverflow,
            overflowStyleName, needsOuterHeight, needsInnerHeight, needsInnerCtPaddingHeight;

        if (width && !ownerContext.widthModel.shrinkWrap &&
            
            
            ((Ext.isIE7m && Ext.isStrict && isShrinkWrapTpl && canOverflowY) ||
            
            
            
            (Ext.isIEQuirks && !isShrinkWrapTpl && !canOverflowX))) {

            if (!manageOverflow) {
                
                
                
                if (canOverflowY && (targetEl.dom.scrollHeight > targetEl.dom.clientHeight)) {
                    
                    width -= scrollbarSize.width;
                }
            }

            ownerContext.outerCtContext.setProp('width', width + padding.width);
            me.hasOuterCtPxWidth = true;
        }

        if (height && !ownerContext.heightModel.shrinkWrap) {
            if (Ext.supports.PercentageHeightOverflowBug) {
                
                
                needsOuterHeight = true;
            }
            if (((Ext.isIE8 && Ext.isStrict) ||
                Ext.isIE7m && Ext.isStrict && isShrinkWrapTpl)) {
                
                
                
                needsInnerHeight = true;
                
                
                needsInnerCtPaddingHeight = !Ext.isIE8;
            }

            if ((needsOuterHeight || needsInnerHeight) && canOverflowX && 
                (targetEl.dom.scrollWidth > targetEl.dom.clientWidth)) {
                
                
                
                height = Math.max(height - scrollbarSize.height, 0);
            }

            if (needsOuterHeight) {
                ownerContext.outerCtContext.setProp('height', height + padding.height);
                me.hasOuterCtPxHeight = true;
            }
            
            if (needsInnerHeight) {
                if (needsInnerCtPaddingHeight) {
                    height += padding.height;
                }
                ownerContext.innerCtContext.setProp('height', height);
                me.hasInnerCtPxHeight = true;
            }
        }

        if (Ext.isIE7 && Ext.isStrict && !isShrinkWrapTpl && (overflowYStyle === 'auto')) {
            
            
            
            
            
            
            
            overflowStyleName = (overflowXStyle === 'auto') ? 'overflow-x' : 'overflow-y';
            targetEl.setStyle(overflowStyleName, 'hidden');
            targetEl.setStyle(overflowStyleName, 'auto');
        }
    },

    setupRenderTpl: function (renderTpl) {
        this.callParent(arguments);

        renderTpl.renderPadding = this.doRenderPadding;
    },

    getContentTarget: function(){
        return this.innerCt;
    }

}, function(){
    this.prototype.chromeCellMeasureBug = Ext.isChrome && Ext.chromeVersion >= 26;
});


Ext.define('Ext.ZIndexManager', {
    alternateClassName: 'Ext.WindowGroup',

    statics: {
        zBase : 9000
    },

    constructor: function(container) {
        var me = this;

        me.map = {};
        me.zIndexStack = [];
        me.front = null;

        if (container) {

            
            if (container.isContainer) {
                container.on('resize', me._onContainerResize, me);
                me.zseed = Ext.Number.from(me.rendered ? container.getEl().getStyle('zIndex') : undefined, me.getNextZSeed());
                
                me.targetEl = container.getTargetEl();
                me.container = container;
            }
            
            else {
                Ext.EventManager.onWindowResize(me._onContainerResize, me);
                me.zseed = me.getNextZSeed();
                me.targetEl = Ext.get(container);
            }
        }
        
        
        else {
            Ext.EventManager.onWindowResize(me._onContainerResize, me);
            me.zseed = me.getNextZSeed();
            Ext.onDocumentReady(function() {
                me.targetEl = Ext.getBody();
            });
        }
    },

    getNextZSeed: function() {
        return (Ext.ZIndexManager.zBase += 10000);
    },

    setBase: function(baseZIndex) {
        this.zseed = baseZIndex;
        var result = this.assignZIndices();
        this._activateLast();
        return result;
    },

    
    assignZIndices: function() {
        var a = this.zIndexStack,
            len = a.length,
            i = 0,
            zIndex = this.zseed,
            comp,
            topModal;

        for (; i < len; i++) {
            comp = a[i];
            if (comp && !comp.hidden) {

                
                
                
                
                
                
                
                zIndex = comp.setZIndex(zIndex);
                if (comp.modal) {
                    topModal = comp;
                }
            }
        }
        
        
        if (topModal) {
            this._showModalMask(topModal)
        }
        return zIndex;
    },

    
    _setActiveChild: function(comp, oldFront) {
        var front = this.front,
            oldPreventFocus = comp.preventFocusOnActivate;

        if (comp !== front) {

            if (front && !front.destroying) {
                front.setActive(false, comp);
            }
            this.front = comp;
            if (comp && comp != oldFront) {

                
                comp.preventFocusOnActivate = comp.preventFocusOnActivate || oldFront && (oldFront.preventFocusOnActivate || !oldFront.focusOnToFront);

                comp.setActive(true);
                
                
                if (comp.modal) {
                    this._showModalMask(comp);
                }

                
                comp.preventFocusOnActivate = oldPreventFocus;
            }
        }
    },

    onComponentHide: function(comp){
        this._activateLast();
    },

    
    _activateLast: function() {
        var me = this,
            stack = me.zIndexStack,
            i = stack.length - 1,
            comp;

        
        
        
        
        
        
        for (; i >= 0 && stack[i].hidden; --i);

        
        if ((comp = stack[i])) {
            me._setActiveChild(comp, me.front);
            if (comp.modal) {
                return;
            }
        }
        
        else {
            if (me.front && !me.front.destroying) {
                me.front.setActive(false);
            }
            me.front = null;
        }

        
        
        for (; i >= 0; --i) {
            comp = stack[i];
            
            if (comp.isVisible() && comp.modal) {
                me._showModalMask(comp);
                return;
            }
        }

        
        
        me._hideModalMask();
    },

    _showModalMask: function(comp) {
        var me = this,
            zIndex = comp.el.getStyle('zIndex') - 4,
            maskTarget = comp.floatParent ? comp.floatParent.getTargetEl() : comp.container,
            mask = me.mask,
            shim = me.maskShim,
            viewSize;

        if (!mask) {
            if (Ext.isIE6) {
                shim = me.maskShim = Ext.getBody().createChild({
                    tag: 'iframe',
                    role: 'presentation',
                    cls : Ext.baseCSSPrefix + 'shim ' + Ext.baseCSSPrefix + 'mask-shim'
                });
                shim.setVisibilityMode(Ext.Element.DISPLAY);
            }

            
            mask = me.mask = Ext.getBody().createChild({
                role: 'presentation',
                cls: Ext.baseCSSPrefix + 'mask',
                style: 'height:0;width:0'
            });
            mask.setVisibilityMode(Ext.Element.DISPLAY);
            mask.on('click', me._onMaskClick, me);
        }
        
        mask.maskTarget = maskTarget;
        viewSize = me.getMaskBox();

        if (shim) {
            shim.setStyle('zIndex', zIndex);
            shim.show();
            shim.setBox(viewSize);
        }
        mask.setStyle('zIndex', zIndex);

        
        
        mask.show();
        mask.setBox(viewSize);
    },

    _hideModalMask: function() {
        var mask = this.mask,
            maskShim = this.maskShim;

        if (mask && mask.isVisible()) {
            mask.maskTarget = undefined;
            mask.hide();
            if (maskShim) {
                maskShim.hide();
            }
        }
    },

    _onMaskClick: function() {
        if (this.front) {
            this.front.focus();
        }
    },
    
    getMaskBox: function(){
        var maskTarget = this.mask.maskTarget;
        if (maskTarget.dom === document.body) {
            return {
                height: Math.max(document.body.scrollHeight, Ext.dom.Element.getDocumentHeight()),
                width: Math.max(document.body.scrollWidth, document.documentElement.clientWidth),
                x: 0,
                y: 0
            };
        } else {
            return maskTarget.getBox();
        } 
    },

    _onContainerResize: function() {
        var me = this,
            mask = me.mask,
            maskShim = me.maskShim,
            viewSize;

        if (mask && mask.isVisible()) {

            
            
            mask.hide();
            if (maskShim) {
                maskShim.hide();
            }

            viewSize = me.getMaskBox();
            if (maskShim) {
                maskShim.setSize(viewSize);
                maskShim.show();
            }
            mask.setSize(viewSize);
            mask.show();
        }
    },

    
    register : function(comp) {
        var me = this,
            compAfterHide = comp.afterHide;
        
        if (comp.zIndexManager) {
            comp.zIndexManager.unregister(comp);
        }
        comp.zIndexManager = me;

        me.map[comp.id] = comp;
        me.zIndexStack.push(comp);
        
        
        comp.afterHide = function() {
            compAfterHide.apply(comp, arguments);
            me.onComponentHide(comp);
        };
    },

    
    unregister : function(comp) {
        var me = this,
            map = me.map;
        
        delete comp.zIndexManager;
        if (map && map[comp.id]) {
            delete map[comp.id];
            
            
            delete comp.afterHide;
            Ext.Array.remove(me.zIndexStack, comp);

            
            me._activateLast();
        }
    },

    
    get : function(id) {
        return id.isComponent ? id : this.map[id];
    },

   
    bringToFront : function(comp, preventFocus) {
        var me = this,
            result = false,
            zIndexStack = me.zIndexStack;
        
        comp = me.get(comp);
        if (comp !== me.front) {
            Ext.Array.remove(zIndexStack, comp);
            if (comp.preventBringToFront) {
                
                zIndexStack.unshift(comp);
            } else {
                
                zIndexStack.push(comp);
            }

            me.assignZIndices();

            
            if (!preventFocus) {
                me._activateLast();
            }
            result = true;
            me.front = comp;
            
            
            if (comp.modal) {
                me._showModalMask(comp);
            }
        }
        return result;
    },

    
    sendToBack : function(comp) {
        var me = this;
        
        comp = me.get(comp);
        Ext.Array.remove(me.zIndexStack, comp);
        me.zIndexStack.unshift(comp);
        me.assignZIndices();
        this._activateLast();
        return comp;
    },

    
    hideAll : function() {
        var map = this.map,
            item,
            id;
            
        for (id in map) {
            if (map.hasOwnProperty(id)) {
                item = map[id];
                if (item.isComponent && item.isVisible()) {
                    item.hide();
                }
            }
        }
    },

    
    hide: function() {
        var i = 0,
            stack = this.zIndexStack,
            len = stack.length,
            comp;

        this.tempHidden = [];
        for (; i < len; i++) {
            comp = stack[i];
            if (comp.isVisible()) {
                this.tempHidden.push(comp);
                comp.el.hide();
                comp.hidden = true;
            }
        }
    },

    
    show: function() {
        var i = 0,
            tempHidden = this.tempHidden,
            len = tempHidden ? tempHidden.length : 0,
            comp;

        for (; i < len; i++) {
            comp = tempHidden[i];
            comp.el.show();
            comp.hidden = false;
            comp.setPosition(comp.x, comp.y);
        }
        delete this.tempHidden;
    },

    
    getActive : function() {
        return this.front;
    },

    
    getBy : function(fn, scope) {
        var r = [],
            i = 0,
            stack = this.zIndexStack,
            len = stack.length,
            comp;

        for (; i < len; i++) {
            comp = stack[i];
            if (fn.call(scope||comp, comp) !== false) {
                r.push(comp);
            }
        }
        return r;
    },

    
    each : function(fn, scope) {
        var map = this.map,
            id,
            comp;
            
        for (id in map) {
            if (map.hasOwnProperty(id)) {
                comp = map[id];
                if (comp.isComponent && fn.call(scope || comp, comp) === false) {
                    return;
                }
            }
        }
    },

    
    eachBottomUp: function (fn, scope) {
        var stack = this.zIndexStack,
            i = 0,
            len = stack.length,
            comp;

        for (; i < len; i++) {
            comp = stack[i];
            if (comp.isComponent && fn.call(scope || comp, comp) === false) {
                return;
            }
        }
    },

    
    eachTopDown: function (fn, scope) {
        var stack = this.zIndexStack,
            i = stack.length,
            comp;

        for (; i-- > 0; ) {
            comp = stack[i];
            if (comp.isComponent && fn.call(scope || comp, comp) === false) {
                return;
            }
        }
    },

    destroy: function() {
        var me   = this,
            map = me.map,
            comp,
            id;

        for (id in map) {
            if (map.hasOwnProperty(id)) {
                comp = map[id];

                if (comp.isComponent) {
                    comp.destroy();
                }
            }
        }

        Ext.destroy(me.mask);
        Ext.destroy(me.maskShim);
        delete me.zIndexStack;
        delete me.map;
        delete me.container;
        delete me.targetEl;
    }
}, function() {
    
    Ext.WindowManager = Ext.WindowMgr = new this();
});


Ext.define('Ext.Queryable', {
    
    isQueryable: true,
    
    
    query : function(selector) {
        selector = selector || '*';
        return Ext.ComponentQuery.query(selector, this);
    },
    
    
    queryBy: function(fn, scope) {
        var out = [],
            items = this.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('#' + id);
    },

    
    child: function (selector) {
        if (selector && selector.isComponent) {
            selector = '#' + Ext.escapeId(selector.getItemId());
        }

        selector = selector || '';
        return this.query('> ' + selector)[0] || null;
    },
    
    
    down: function (selector) {
        if (selector && selector.isComponent) {
            selector = '#' + Ext.escapeId(selector.getItemId());
        }

        selector = selector || '';
        return this.query(selector)[0] || null;
    },
    
    getRefItems: function(){
        return [];
    }
        
});


Ext.define('Ext.layout.component.Component', {

    

    extend:  Ext.layout.Layout ,

    

    type: 'component',

    isComponentLayout: true,

    nullBox: {},

    usesContentHeight: true,
    usesContentWidth: true,
    usesHeight: true,
    usesWidth: true,

    beginLayoutCycle: function (ownerContext, firstCycle) {
        var me = this,
            owner = me.owner,
            ownerCtContext = ownerContext.ownerCtContext,
            heightModel = ownerContext.heightModel,
            widthModel = ownerContext.widthModel,
            body = owner.el.dom === document.body,
            lastBox = owner.lastBox || me.nullBox,
            lastSize = owner.el.lastBox || me.nullBox,
            dirty = !body,
            ownerLayout, v, widthName, heightName;

        me.callParent(arguments);

        if (firstCycle) {
            if (me.usesContentWidth) {
                ++ownerContext.consumersContentWidth;
            }
            if (me.usesContentHeight) {
                ++ownerContext.consumersContentHeight;
            }
            if (me.usesWidth) {
                ++ownerContext.consumersWidth;
            }
            if (me.usesHeight) {
                ++ownerContext.consumersHeight;
            }

            if (ownerCtContext && !ownerCtContext.hasRawContent) {
                ownerLayout = owner.ownerLayout;

                if (ownerLayout.usesWidth) {
                    ++ownerContext.consumersWidth;
                }
                if (ownerLayout.usesHeight) {
                    ++ownerContext.consumersHeight;
                }
            }
        }

        
        

        if (widthModel.configured) {
            
            
            
            
            widthName = widthModel.names.width;

            if (!body) {
                dirty = me.setWidthInDom ||
                        (firstCycle ? owner[widthName] !== lastSize.width : widthModel.constrained);
            }

            ownerContext.setWidth(owner[widthName], dirty);
        } else if (ownerContext.isTopLevel) {
            if (widthModel.calculated) {
                v = lastBox.width;
                ownerContext.setWidth(v, v != lastSize.width);
            }

            v = lastBox.x;
            ownerContext.setProp('x', v, v != lastSize.x);
        }

        if (heightModel.configured) {
            heightName = heightModel.names.height;

            if (!body) {
                dirty = firstCycle ? owner[heightName] !== lastSize.height
                                   : heightModel.constrained;
            }

            ownerContext.setHeight(owner[heightName], dirty);
        } else if (ownerContext.isTopLevel) {
            if (heightModel.calculated) {
                v = lastBox.height;
                ownerContext.setHeight(v, v != lastSize.height);
            }

            v = lastBox.y;
            ownerContext.setProp('y', v, v != lastSize.y);
        }
    },

    finishedLayout: function(ownerContext) {
        var me = this,
            elementChildren = ownerContext.children,
            owner = me.owner,
            len, i, elContext, lastBox, props;

        

        
        
        if (elementChildren) {
            len = elementChildren.length;
            for (i = 0; i < len; i++) {
                elContext = elementChildren[i];
                elContext.el.lastBox = elContext.props;
            }
        }

        
        ownerContext.previousSize = me.lastComponentSize;

        
        me.lastComponentSize = owner.el.lastBox = props = ownerContext.props;
        
        
        
        lastBox = owner.lastBox || (owner.lastBox = {});
        lastBox.x = props.x;
        lastBox.y = props.y;
        lastBox.width = props.width;
        lastBox.height = props.height;
        lastBox.invalid = false;
        
        me.callParent(arguments);
    },
    
    notifyOwner: function(ownerContext) {
        var me = this,
            currentSize = me.lastComponentSize,
            prevSize = ownerContext.previousSize,
            args = [currentSize.width, currentSize.height];

        if (prevSize) {
            args.push(prevSize.width, prevSize.height);
        }

        
        me.owner.afterComponentLayout.apply(me.owner, args);
    },

    
    getTarget : function() {
        return this.owner.el;
    },

    
    getRenderTarget : function() {
        return this.owner.el;
    },

    cacheTargetInfo: function(ownerContext) {
        var me = this,
            targetInfo = me.targetInfo,
            target;

        if (!targetInfo) {
            target = ownerContext.getEl('getTarget', me);

            me.targetInfo = targetInfo = {
                padding: target.getPaddingInfo(),
                border: target.getBorderInfo()
            };
        }

        return targetInfo;
    },

    measureAutoDimensions: function (ownerContext, dimensions) {
        
        
        
        
        

        var me = this,
            owner = me.owner,
            containerLayout = owner.layout,
            heightModel = ownerContext.heightModel,
            widthModel = ownerContext.widthModel,
            boxParent = ownerContext.boxParent,
            isBoxParent = ownerContext.isBoxParent,
            props = ownerContext.props,
            isContainer,
            ret = {
                gotWidth: false,
                gotHeight: false,
                isContainer: (isContainer = !ownerContext.hasRawContent)
            },
            hv = dimensions || 3,
            zeroWidth, zeroHeight,
            needed = 0,
            got = 0,
            ready, size, temp;

        
        

        
        

        if (widthModel.shrinkWrap && ownerContext.consumersContentWidth) {
            ++needed;
            zeroWidth = !(hv & 1);

            if (isContainer) {
                
                
                if (zeroWidth) {
                    ret.contentWidth = 0;
                    ret.gotWidth = true;
                    ++got;
                } else if ((ret.contentWidth = ownerContext.getProp('contentWidth')) !== undefined) {
                    ret.gotWidth = true;
                    ++got;
                }
            } else {
                size = props.contentWidth;

                if (typeof size == 'number') { 
                    ret.contentWidth = size;
                    ret.gotWidth = true;
                    ++got;
                } else {
                    if (zeroWidth) {
                        ready = true;
                    } else if (!ownerContext.hasDomProp('containerChildrenSizeDone')) {
                        ready = false;
                    } else if (isBoxParent || !boxParent || boxParent.widthModel.shrinkWrap) {
                        
                        
                        
                        ready = true;
                    } else {
                        
                        
                        
                        ready = boxParent.hasDomProp('width');
                    }

                    if (ready) {
                        if (zeroWidth) {
                            temp = 0;
                        } else if (containerLayout && containerLayout.measureContentWidth) {
                            
                            
                            temp = containerLayout.measureContentWidth(ownerContext);
                        } else {
                            temp = me.measureContentWidth(ownerContext);
                        }

                        if (!isNaN(ret.contentWidth = temp)) {
                            ownerContext.setContentWidth(temp, true);
                            ret.gotWidth = true;
                            ++got;
                        }
                    }
                }
            }
        } else if (widthModel.natural && ownerContext.consumersWidth) {
            ++needed;
            size = props.width;
            

            if (typeof size == 'number') { 
                ret.width = size;
                ret.gotWidth = true;
                ++got;
            } else {
                if (isBoxParent || !boxParent) {
                    ready = true;
                } else {
                    
                    
                    
                    ready = boxParent.hasDomProp('width');
                }

                if (ready) {
                    if (!isNaN(ret.width = me.measureOwnerWidth(ownerContext))) {
                        ownerContext.setWidth(ret.width, false);
                        ret.gotWidth = true;
                        ++got;
                    }
                }
            }
        }

        
        

        if (heightModel.shrinkWrap && ownerContext.consumersContentHeight) {
            ++needed;
            zeroHeight = !(hv & 2);

            if (isContainer) {
                
                if (zeroHeight) {
                    ret.contentHeight = 0;
                    ret.gotHeight = true;
                    ++got;
                } else if ((ret.contentHeight = ownerContext.getProp('contentHeight')) !== undefined) {
                    ret.gotHeight = true;
                    ++got;
                }
            } else {
                size = props.contentHeight;

                if (typeof size == 'number') { 
                    ret.contentHeight = size;
                    ret.gotHeight = true;
                    ++got;
                } else {
                    if (zeroHeight) {
                        ready = true;
                    } else if (!ownerContext.hasDomProp('containerChildrenSizeDone')) {
                        ready = false;
                    } else if (owner.noWrap) {
                        ready = true;
                    } else if (!widthModel.shrinkWrap) {
                        
                        ready = (ownerContext.bodyContext || ownerContext).hasDomProp('width');
                    } else if (isBoxParent || !boxParent || boxParent.widthModel.shrinkWrap) {
                        
                        
                        
                        ready = true;
                    } else {
                        
                        
                        
                        ready = boxParent.hasDomProp('width');
                    }

                    if (ready) {
                        if (zeroHeight) {
                            temp = 0;
                        } else if (containerLayout && containerLayout.measureContentHeight) {
                            
                            
                            temp = containerLayout.measureContentHeight(ownerContext);
                        } else {
                            temp = me.measureContentHeight(ownerContext);
                        }

                        if (!isNaN(ret.contentHeight = temp)) {
                            ownerContext.setContentHeight(temp, true);
                            ret.gotHeight = true;
                            ++got;
                        }
                    }
                }
            }
        } else if (heightModel.natural && ownerContext.consumersHeight) {
            ++needed;
            size = props.height;
            

            if (typeof size == 'number') { 
                ret.height = size;
                ret.gotHeight = true;
                ++got;
            } else {
                if (isBoxParent || !boxParent) {
                    ready = true;
                } else {
                    
                    
                    
                    ready = boxParent.hasDomProp('width');
                }

                if (ready) {
                    if (!isNaN(ret.height = me.measureOwnerHeight(ownerContext))) {
                        ownerContext.setHeight(ret.height, false);
                        ret.gotHeight = true;
                        ++got;
                    }
                }
            }
        }

        if (boxParent) {
            ownerContext.onBoxMeasured();
        }

        ret.gotAll = got == needed;
        
        return ret;
    },

    measureContentWidth: function (ownerContext) {
        
        return ownerContext.el.getWidth() - ownerContext.getFrameInfo().width;
    },

    measureContentHeight: function (ownerContext) {
        
        return ownerContext.el.getHeight() - ownerContext.getFrameInfo().height;
    },

    measureOwnerHeight: function (ownerContext) {
        return ownerContext.el.getHeight();
    },

    measureOwnerWidth: function (ownerContext) {
        return ownerContext.el.getWidth();
    }
});


Ext.define('Ext.layout.component.Auto', {

    

    alias: 'layout.autocomponent',

    extend:  Ext.layout.component.Component ,

    

    type: 'autocomponent',

    
    setHeightInDom: false,

    
    setWidthInDom: false,

    waitForOuterHeightInDom: false,
    waitForOuterWidthInDom: false,
    
    beginLayoutCycle: function(ownerContext, firstCycle){
        var me = this,
            lastWidthModel = me.lastWidthModel,
            lastHeightModel = me.lastHeightModel,
            el = me.owner.el;
            
        me.callParent(arguments);
            
        if (lastWidthModel && lastWidthModel.fixed && ownerContext.widthModel.shrinkWrap) {
            el.setWidth(null);
        }
            
        if (lastHeightModel && lastHeightModel.fixed && ownerContext.heightModel.shrinkWrap) {
            el.setHeight(null);
        }    
    },

    calculate: function(ownerContext) {
        var me = this,
            measurement = me.measureAutoDimensions(ownerContext),
            heightModel = ownerContext.heightModel,
            widthModel = ownerContext.widthModel,
            width, height;

        
        
        if (measurement.gotWidth) {
            if (widthModel.shrinkWrap) {
                me.publishOwnerWidth(ownerContext, measurement.contentWidth);
            } else if (me.publishInnerWidth) {
                me.publishInnerWidth(ownerContext, measurement.width);
            }
        } else if (!widthModel.auto && me.publishInnerWidth) {
            width = me.waitForOuterWidthInDom ? ownerContext.getDomProp('width')
                        : ownerContext.getProp('width');
            if (width === undefined) {
                me.done = false;
            } else {
                me.publishInnerWidth(ownerContext, width);
            }
        }

        if (measurement.gotHeight) {
            if (heightModel.shrinkWrap) {
                me.publishOwnerHeight(ownerContext, measurement.contentHeight);
            } else if (me.publishInnerHeight) {
                me.publishInnerHeight(ownerContext, measurement.height);
            }
        } else if (!heightModel.auto && me.publishInnerHeight) {
            height = me.waitForOuterHeightInDom ? ownerContext.getDomProp('height')
                        : ownerContext.getProp('height');
            if (height === undefined) {
                me.done = false;
            } else {
               me.publishInnerHeight(ownerContext, height);
            }
        }

        if (!measurement.gotAll) {
            me.done = false;
        }
    },

    calculateOwnerHeightFromContentHeight: function (ownerContext, contentHeight) {
        return contentHeight + ownerContext.getFrameInfo().height;
    },

    calculateOwnerWidthFromContentWidth: function (ownerContext, contentWidth) {
        return contentWidth + ownerContext.getFrameInfo().width;
    },

    publishOwnerHeight: function (ownerContext, contentHeight) {
        var me = this,
            owner = me.owner,
            height = me.calculateOwnerHeightFromContentHeight(ownerContext, contentHeight),
            constrainedHeight, dirty, heightModel;

        if (isNaN(height)) {
            me.done = false;
        } else {
            constrainedHeight = Ext.Number.constrain(height, owner.minHeight, owner.maxHeight);

            if (constrainedHeight == height) {
                dirty = me.setHeightInDom;
            } else {
                heightModel = me.sizeModels[
                    (constrainedHeight < height) ? 'constrainedMax' : 'constrainedMin'];
                height = constrainedHeight;

                if (ownerContext.heightModel.calculatedFromShrinkWrap) {
                    
                    
                    
                    ownerContext.heightModel = heightModel;
                } else {
                    ownerContext.invalidate({ heightModel: heightModel });
                }
            }
            
            ownerContext.setHeight(height, dirty);
        }
    },

    publishOwnerWidth: function (ownerContext, contentWidth) {
        var me = this,
            owner = me.owner,
            width = me.calculateOwnerWidthFromContentWidth(ownerContext, contentWidth),
            constrainedWidth, dirty, widthModel;

        if (isNaN(width)) {
            me.done = false;
        } else {
            constrainedWidth = Ext.Number.constrain(width, owner.minWidth, owner.maxWidth);

            if (constrainedWidth == width) {
                dirty = me.setWidthInDom;
            } else {
                widthModel = me.sizeModels[
                    (constrainedWidth < width) ? 'constrainedMax' : 'constrainedMin'];
                width = constrainedWidth;

                if (ownerContext.widthModel.calculatedFromShrinkWrap) {
                    
                    
                    
                    ownerContext.widthModel = widthModel;
                } else {
                    ownerContext.invalidate({ widthModel: widthModel });
                }
            }

            ownerContext.setWidth(width, dirty);
        }
    }
});


Ext.define('Ext.container.AbstractContainer', {

    

    extend:  Ext.Component ,

               
                                   
                                    
                           
      

    mixins: {
        queryable:  Ext.Queryable 
    },

    

    renderTpl: '{%this.renderContainer(out,values)%}',

    

    

    

    

    
    suspendLayout : false,

    
    autoDestroy : true,

     
    defaultType: 'panel',

    
    detachOnRemove: true,

    
    isContainer : true,

    
    layoutCounter : 0,

    baseCls: Ext.baseCSSPrefix + 'container',

    

    defaultLayoutType: 'auto',
    
    ariaRole: 'presentation',

    
    initComponent : function(){
        var me = this;
        me.addEvents(
            
            'afterlayout',
            
            'beforeadd',
            
            'beforeremove',
            
            'add',
            
            'remove'
        );

        me.callParent();

        me.getLayout();
        me.initItems();
    },

    
    initItems : function() {
        var me = this,
            items = me.items;

        
        me.items = new Ext.util.AbstractMixedCollection(false, me.getComponentId);
        me.floatingItems = new Ext.util.MixedCollection(false, me.getComponentId);

        if (items) {
            if (!Ext.isArray(items)) {
                items = [items];
            }

            me.add(items);
        }
    },

    
    getFocusEl: function() {
        return this.getTargetEl();
    },

    finishRenderChildren: function () {
        this.callParent();

        var layout = this.getLayout();

        if (layout) {
            layout.finishRender();
        }
    },

    beforeRender: function () {
        var me = this,
            layout = me.getLayout(),
            targetCls;

        me.callParent();

        if (!layout.initialized) {
            layout.initLayout();
        }

        targetCls = layout.targetCls;

        if (targetCls) {
            me.applyTargetCls(targetCls);
        }
    },

    
    
    
    
    
    applyTargetCls: function(targetCls) {
        this.addCls(targetCls);
    },

    afterComponentLayout: function() {
        var floaters = this.floatingItems.items,
            floaterCount = floaters.length,
            i, floater

        this.callParent(arguments);

        
        for (i = 0; i < floaterCount; i++) {
            floater = floaters[i];
            if (!floater.rendered && floater.autoShow) {
                floater.show();
            }
        }
    },

    onPosition: function() {
        this.callParent(arguments);
        this.repositionFloatingItems();
    },

    onResize: function() {
        this.callParent(arguments);
        this.repositionFloatingItems();
    },

    repositionFloatingItems: function() {
        var floaters = this.floatingItems.items,
            floaterCount = floaters.length,
            i, floater;

        
        for (i = 0; i < floaterCount; i++) {
            floater = floaters[i];
            if (floater.el && !floater.hidden) {
                floater.setPosition(floater.x, floater.y);
            }
        }
    },

    setupRenderTpl: function (renderTpl) {
        this.callParent(arguments);
        this.getLayout().setupRenderTpl(renderTpl);
    },

    
    getDefaultContentTarget: function() {
        return this.el;
    },

    
    getContentTarget: function(){
        return this.getLayout().getContentTarget();
    },

    
    setLayout : function(layout) {
        var currentLayout = this.layout;

        if (currentLayout && currentLayout.isLayout && currentLayout != layout) {
            currentLayout.setOwner(null);
        }

        this.layout = layout;
        layout.setOwner(this);
    },

    
    getLayout : function() {
        var me = this;
        if (!me.layout || !me.layout.isLayout) {
            
            me.setLayout(Ext.layout.Layout.create(me.layout, me.self.prototype.layout || me.defaultLayoutType));
        }

        return me.layout;
    },

    
    doLayout : function() {
        this.updateLayout();
        return this;
    },

    
    afterLayout : function(layout) {
        var me = this;
        ++me.layoutCounter;
        if (me.hasListeners.afterlayout) {
            me.fireEvent('afterlayout', me, layout);
        }
    },

    
    prepareItems : function(items, applyDefaults) {
        
        
        if (Ext.isArray(items)) {
            items = items.slice();
        } else {
            items = [items];
        }

        
        var me = this,
            i = 0,
            len = items.length,
            item;

        for (; i < len; i++) {
            item = items[i];
            if (item == null) {
                Ext.Array.erase(items, i, 1);
                --i;
                --len;
            } else {
                if (applyDefaults) {
                    item = this.applyDefaults(item);
                }

                
                item.isContained = me;
                items[i] = me.lookupComponent(item);
                
                delete item.isContained;
                delete items[i].isContained;
            }
        }

        return items;
    },

    
    applyDefaults : function(config) {
        var defaults = this.defaults;

        if (defaults) {
            if (Ext.isFunction(defaults)) {
                defaults = defaults.call(this, config);
            }

            if (Ext.isString(config)) {
                config = Ext.ComponentManager.get(config);
            }
            Ext.applyIf(config, defaults);
        }
        return config;
    },

    
    lookupComponent : function(comp) {
        return (typeof comp == 'string') ? Ext.ComponentManager.get(comp)
                                         : Ext.ComponentManager.create(comp, this.defaultType);
    },

    
    getComponentId : function(comp) {
        return comp.getItemId && comp.getItemId();
    },

    
    add : function() {
        var me = this,
            args = Ext.Array.slice(arguments),
            index = (typeof args[0] == 'number') ? args.shift() : -1,
            layout = me.getLayout(),
            addingArray, items, i, length, item, pos, ret,
            needsLayout = false;

        if (args.length == 1 && Ext.isArray(args[0])) {
            items = args[0];
            addingArray = true;
        } else {
            items = args;
        }

        if (me.rendered) {
            Ext.suspendLayouts(); 
        }

        ret = items = me.prepareItems(items, true);
        length = items.length;

        if (!addingArray && length == 1) { 
            ret = items[0];
        }

        
        for (i = 0; i < length; i++) {
            item = items[i];

            pos = (index < 0) ? me.items.length : (index + i);

            
            if (item.floating) {
                me.floatingItems.add(item);
                item.onAdded(me, pos);

                if (me.hasListeners.add) {
                    me.fireEvent('add', me, item, pos);
                }
            } else if ((!me.hasListeners.beforeadd || me.fireEvent('beforeadd', me, item, pos) !== false) && me.onBeforeAdd(item) !== false) {
                me.items.insert(pos, item);
                item.onAdded(me, pos);
                me.onAdd(item, pos);
                layout.onAdd(item, pos);

                needsLayout = true;
                if (me.hasListeners.add) {
                    me.fireEvent('add', me, item, pos);
                }
            }
        }

        
        
        if (needsLayout) {
            me.updateLayout();
        }
        if (me.rendered) {
            Ext.resumeLayouts(true);
        }

        return ret;
    },

    
    onAdd : Ext.emptyFn,

    
    onRemove : Ext.emptyFn,

    
    insert : function(index, comp) {
        var compIdx;
        if (comp && comp.isComponent) {
            compIdx = this.items.indexOf(comp);
            if (compIdx !== -1) {
                return this.move(compIdx, index);
            }
        }
        return this.add(index, comp);
    },

    
    move : function(fromIdx, toIdx) {
        var items = this.items,
            item;

        if (fromIdx.isComponent) {
            fromIdx = items.indexOf(fromIdx);
        }
        item = items.removeAt(fromIdx);
        if (item === false) {
            return false;
        }
        items.insert(toIdx, item);
        this.onMove(item, fromIdx, toIdx);
        this.updateLayout();
        return item;
    },

    onMove: Ext.emptyFn,

    
    onBeforeAdd : function(item) {
        
        if (item.ownerCt && item.ownerCt !== this) {
            item.ownerCt.remove(item, false);
        }
    },

    
    remove : function(comp, autoDestroy) {
        var me = this,
            c = me.getComponent(comp);

        if (c && (!me.hasListeners.beforeremove || me.fireEvent('beforeremove', me, c) !== false)) {
            me.doRemove(c, autoDestroy);
            if (me.hasListeners.remove) {
                me.fireEvent('remove', me, c);
            }

            if (!me.destroying && !c.floating) {
                me.updateLayout();
            }
        }

        return c;
    },

    
    doRemove : function(component, doDestroy) {
        
        doDestroy = doDestroy === true || (doDestroy !== false && this.autoDestroy);

        var me = this,
            layout = me.layout,
            hasLayout = layout && me.rendered,

            
            isDestroying = component.destroying || doDestroy,
            floating = component.floating;

        if (floating) {
            me.floatingItems.remove(component);
        } else {
            me.items.remove(component);
        }

        
        if (hasLayout && !floating) {
            
            if (layout.running) {
                Ext.AbstractComponent.cancelLayout(component, isDestroying);
            }
            layout.onRemove(component, isDestroying);
        }

        component.onRemoved(isDestroying);

        me.onRemove(component, isDestroying);

        
        if (doDestroy) {
            component.destroy();
        }
        
        else {
            if (hasLayout && !floating) {
                layout.afterRemove(component);
            }
            if (me.detachOnRemove && component.rendered) {
                me.detachComponent(component);
            }
        }
    },

    
    detachComponent: function(component){
        Ext.getDetachedBody().appendChild(component.getEl());
    },

    
    removeAll : function(autoDestroy) {
        var me = this,
            removeItems = me.items.items.slice().concat(me.floatingItems.items),
            items = [],
            i = 0,
            len = removeItems.length,
            item;

        
        me.suspendLayouts();
        for (; i < len; i++) {
            item = removeItems[i];
            me.remove(item, autoDestroy);

            if (item.ownerCt !== me) {
                items.push(item);
            }
        }

        
        me.resumeLayouts(!!len);
        return items;
    },

    
    getRefItems : function(deep) {
        var me = this,
            items = me.items.items,
            len = items.length,
            i = 0,
            item,
            result = [];

        for (; i < len; i++) {
            item = items[i];
            result[result.length] = item;
            if (deep && item.getRefItems) {
                result.push.apply(result, item.getRefItems(true));
            }
        }

        
        items = me.floatingItems.items;
        len = items.length;
        for (i = 0; i < len; i++) {
            item = items[i];
            result[result.length] = item;
            if (deep && item.getRefItems) {
                result.push.apply(result, item.getRefItems(true));
            }
        }

        return result;
    },

    
    cascade : function(fn, scope, origArgs){
        var me = this,
            cs = me.items ? me.items.items : [],
            len = cs.length,
            i = 0,
            c,
            args = origArgs ? origArgs.concat(me) : [me],
            componentIndex = args.length - 1;

        if (fn.apply(scope || me, args) !== false) {
            for (; i < len; i++){
                c = cs[i];
                if (c.cascade) {
                    c.cascade(fn, scope, origArgs);
                } else {
                    args[componentIndex] = c;
                    fn.apply(scope || cs, args);
                }
            }
        }
        return this;
    },

    
    isAncestor: function(possibleDescendant) {
        while (possibleDescendant) {
            if (possibleDescendant.ownerCt === this) {
                return true;
            }
            possibleDescendant = possibleDescendant.ownerCt;
        }
    },

    
    getComponent : function(comp) {
        if (Ext.isObject(comp)) {
            comp = comp.getItemId();
        }

        var c = this.items.get(comp);

        
        if (!c && typeof comp != 'number') {
            c = this.floatingItems.get(comp);
        }

        return c;
    },

    
    contains: function(comp, deep) {
        var result = false;
        if (deep) {
            this.cascade(function(c) {
                
                if (c.contains && c.contains(comp)) {
                    result = true;
                    return false;
                }
            });
            return result;
        } else {
            return this.items.contains(comp) || this.floatingItems.contains(comp);
        }
    },

    nextChild: function(child, selector) {
        var me = this,
            result,
            childIndex = me.items.indexOf(child);

        if (childIndex !== -1) {
            result = selector ? Ext.ComponentQuery(selector, me.items.items.slice(childIndex + 1)) : me.items.getAt(childIndex + 1);
            if (!result && me.ownerCt) {
                result = me.ownerCt.nextChild(me, selector);
            }
        }
        return result;
    },

    prevChild: function(child, selector) {
        var me = this,
            result,
            childIndex = me.items.indexOf(child);

        if (childIndex !== -1) {
            result = selector ? Ext.ComponentQuery(selector, me.items.items.slice(childIndex + 1)) : me.items.getAt(childIndex + 1);
            if (!result && me.ownerCt) {
                result = me.ownerCt.nextChild(me, selector);
            }
        }
        return result;
    },

    
    
    
    enable: function() {
        this.callParent(arguments);

        var itemsToDisable = this.getChildItemsToDisable(),
            length         = itemsToDisable.length,
            item, i;

        for (i = 0; i < length; i++) {
            item = itemsToDisable[i];

            if (item.resetDisable) {
                item.enable();
            }
        }

        return this;
    },

    
    
    
    disable: function() {
        this.callParent(arguments);

        var itemsToDisable = this.getChildItemsToDisable(),
            length         = itemsToDisable.length,
            item, i;

        for (i = 0; i < length; i++) {
            item = itemsToDisable[i];

            if (item.resetDisable !== false && !item.disabled) {
                item.disable();
                item.resetDisable = true;
            }
        }

        return this;
    },

    
    getChildItemsToDisable: function(){
        return this.query('[isFormField],button');
    },

    
    
    beforeDestroy : function() {
        var me = this,
            items = me.items,
            floatingItems = me.floatingItems,
            c;

        if (items) {
            while ((c = items.first())) {
                me.doRemove(c, true);
            }
        }

        if (floatingItems) {
            while ((c = floatingItems.first())) {
                me.doRemove(c, true);
            }
        }

        Ext.destroy(
            me.layout
        );
        me.callParent();
    }
});


Ext.define('Ext.container.Container', {
    extend:  Ext.container.AbstractContainer ,
    alias: 'widget.container',
    alternateClassName: 'Ext.Container',

    
    getChildByElement: function(el, deep) {
        var item,
            itemEl,
            i = 0,
            it = this.getRefItems(),
            ln = it.length;

        el = Ext.getDom(el);
        for (; i < ln; i++) {
            item = it[i];
            itemEl = item.getEl();
            if (itemEl && ((itemEl.dom === el) || itemEl.contains(el))) {
                return (deep && item.getChildByElement) ? item.getChildByElement(el, deep) : item;
            }
        }
        return null;
    }
});


Ext.define('Ext.layout.container.Editor', {

    

    alias: 'layout.editor',

    extend:  Ext.layout.container.Container ,

    

    autoSizeDefault: {
        width: 'field',
        height: 'field'    
    },

    sizePolicies: {
        
        $: {
            
            $: {
                readsWidth: 1,
                readsHeight: 1,
                setsWidth: 0,
                setsHeight: 0
            },
            boundEl: {
                readsWidth: 1,
                readsHeight: 0,
                setsWidth: 0,
                setsHeight: 1
            }
        },

        boundEl: {
            
            $: {
                readsWidth: 0,
                readsHeight: 1,
                setsWidth: 1,
                setsHeight: 0
            },
            boundEl: {
                readsWidth: 0,
                readsHeight: 0,
                setsWidth: 1,
                setsHeight: 1
            }
        }
    },

    getItemSizePolicy: function (item) {
        var me = this,
            autoSize = me.owner.autoSize,
            key = autoSize && autoSize.width,
            policy = me.sizePolicies;

        policy = policy[key] || policy.$;

        key = autoSize && autoSize.height;
        policy = policy[key] || policy.$;

        return policy;
    },

    calculate: function(ownerContext) {
        var me = this,
            owner = me.owner,
            autoSize = owner.autoSize,
            fieldWidth,
            fieldHeight;
            
        if (autoSize === true) {
            autoSize = me.autoSizeDefault;
        }

        
        if (autoSize) {
            fieldWidth  = me.getDimension(owner, autoSize.width,  'getWidth',  owner.width);
            fieldHeight = me.getDimension(owner, autoSize.height, 'getHeight', owner.height);
        }

        
        ownerContext.childItems[0].setSize(fieldWidth, fieldHeight);

        
        ownerContext.setWidth(fieldWidth);
        ownerContext.setHeight(fieldHeight);

        
        ownerContext.setContentSize(fieldWidth || owner.field.getWidth(),
                                    fieldHeight || owner.field.getHeight());
    },

    getDimension: function(owner, type, getMethod, ownerSize){
        switch (type) {
            
            case 'boundEl':
                return owner.boundEl[getMethod]();

            
            case 'field':
                return undefined;

            
            default:
                return ownerSize;
        }
    }
});


Ext.define('Ext.Editor', {

    

    extend:  Ext.container.Container ,

    alias: 'widget.editor',

                                              

    

   layout: 'editor',

    

    
    allowBlur: true,

    

    
    revertInvalid: true,

    

    

    
    value : '',

    
    alignment: 'c-c?',

    
    offsets: [0, 0],

    
    shadow : 'frame',

    
    constrain : false,

    
    swallowKeys : true,

    
    completeOnEnter : true,

    
    cancelOnEsc : true,

    
    updateEl : false,

    
    
    
    focusOnToFront: false,

    

    
    hidden: true,
    baseCls: Ext.baseCSSPrefix + 'editor',

    initComponent : function() {
        var me = this,
            field = me.field = Ext.ComponentManager.create(me.field, 'textfield');

        field.inEditor = true;
        field.msgTarget = field.msgTarget || 'qtip';
        me.mon(field, {
            scope: me,
            blur: me.onFieldBlur,
            specialkey: me.onSpecialKey
        });

        if (field.grow) {
            me.mon(field, 'autosize', me.onFieldAutosize,  me, {delay: 1});
        }
        me.floating = {
            constrain: me.constrain
        };
        me.items = field;

        me.callParent(arguments);

        me.addEvents(
            
            'beforestartedit',

            
            'startedit',

            
            'beforecomplete',
            
            'complete',
            
            'canceledit',
            
            'specialkey'
        );
    },

    
    onFieldAutosize: function(){
        this.updateLayout();
    },

    
    afterRender : function(ct, position) {
        var me = this,
            field = me.field,
            inputEl = field.inputEl;

        me.callParent(arguments);

        
        if (inputEl) {
            inputEl.dom.name = '';
            if (me.swallowKeys) {
                inputEl.swallowEvent([
                    'keypress', 
                    'keydown'   
                ]);
            }
        }
    },

    
    onSpecialKey : function(field, event) {
        var me = this,
            key = event.getKey(),
            complete = me.completeOnEnter && key == event.ENTER,
            cancel = me.cancelOnEsc && key == event.ESC;

        if (complete || cancel) {
            event.stopEvent();
            
            
            Ext.defer(function() {
                
                if (complete) {
                    me.completeEdit();
                } else {
                    me.cancelEdit();
                }
            }, 1);
        }

        me.fireEvent('specialkey', me, field, event);
    },

    
    startEdit : function(el, value) {
        var me = this,
            field = me.field;

        me.completeEdit();
        me.boundEl = Ext.get(el);
        value = Ext.isDefined(value) ? value : Ext.String.trim(me.boundEl.dom.innerText || me.boundEl.dom.innerHTML);

        if (!me.rendered) {
            
            
            
            
            if (me.ownerCt) {
                me.parentEl = me.ownerCt.el;
                me.parentEl.position();
            }
            me.render(me.parentEl || document.body);
        }

        if (me.fireEvent('beforestartedit', me, me.boundEl, value) !== false) {
            me.startValue = value;
            me.show();
            
            field.suspendEvents();
            field.reset();
            field.setValue(value);
            field.resumeEvents();
            me.realign(true);
            field.focus([field.getRawValue().length]);
            if (field.autoSize) {
                field.autoSize();
            }
            me.editing = true;
        }
    },

    
    realign : function(autoSize) {
        var me = this;
        if (autoSize === true) {
            me.updateLayout();
        }
        me.alignTo(me.boundEl, me.alignment, me.offsets);
    },

    
    completeEdit : function(remainVisible) {
        var me = this,
            field = me.field,
            value;

        if (!me.editing) {
            return;
        }

        
        if (field.assertValue) {
            field.assertValue();
        }

        value = me.getValue();
        if (!field.isValid()) {
            if (me.revertInvalid !== false) {
                me.cancelEdit(remainVisible);
            }
            return;
        }

        if (String(value) === String(me.startValue) && me.ignoreNoChange) {
            me.hideEdit(remainVisible);
            return;
        }

        if (me.fireEvent('beforecomplete', me, value, me.startValue) !== false) {
            
            value = me.getValue();
            if (me.updateEl && me.boundEl) {
                me.boundEl.update(value);
            }
            me.hideEdit(remainVisible);
            me.fireEvent('complete', me, value, me.startValue);
        }
    },

    
    onShow : function() {
        var me = this;

        me.callParent(arguments);
        if (me.hideEl !== false) {
            me.boundEl.hide();
        }
        me.fireEvent('startedit', me, me.boundEl, me.startValue);
    },

    
    cancelEdit : function(remainVisible) {
        var me = this,
            startValue = me.startValue,
            field = me.field,
            value;

        if (me.editing) {
            if (field) {
                value = me.editedValue = me.getValue();
                
                field.suspendEvents();
                me.setValue(startValue);
                field.resumeEvents();
            }
            me.hideEdit(remainVisible);
            me.fireEvent('canceledit', me, value, startValue);
            delete me.editedValue;
        }
    },

    
    hideEdit: function(remainVisible) {
        if (remainVisible !== true) {
            this.editing = false;
            this.hide();
        }
    },

    
    onFieldBlur : function(field, e) {
        var me = this,
            target = Ext.Element.getActiveElement();

        
        if(me.allowBlur === true && me.editing && me.selectSameEditor !== true) {
            me.completeEdit();
        }

        
        if (Ext.fly(target).isFocusable() || target.getAttribute('tabIndex')) {
            target.focus();
        }
    },

    
    onHide : function() {
        var me = this,
            field = me.field;

        if (me.editing) {
            me.completeEdit();
            return;
        }
        
        
        if (field.hasFocus && field.triggerBlur) {
            field.triggerBlur();
        }
        if (field.collapse) {
            field.collapse();
        }

        
        if (me.hideEl !== false) {
            me.boundEl.show();
        }
        me.callParent(arguments);
    },

    
    setValue : function(value) {
        this.field.setValue(value);
    },

    
    getValue : function() {
        return this.field.getValue();
    },

    beforeDestroy : function() {
        var me = this;

        Ext.destroy(me.field);
        delete me.field;
        delete me.parentEl;
        delete me.boundEl;

        me.callParent(arguments);
    }
});


Ext.define('Ext.util.KeyMap', {
    alternateClassName: 'Ext.KeyMap',

    

    

    

    

    
    eventName: 'keydown',

    constructor: function(config) {
        var me = this;

        
        
        if ((arguments.length !== 1) || (typeof config === 'string') || config.dom || config.tagName || config === document || config.isComponent) {
            me.legacyConstructor.apply(me, arguments);
            return;
        }

        Ext.apply(me, config);
        me.bindings = [];

        if (!me.target.isComponent) {
            me.target = Ext.get(me.target);
        }

        if (me.binding) {
            me.addBinding(me.binding);
        } else if (config.key) {
            me.addBinding(config);
        }
        me.enable();
    },

    
    legacyConstructor: function(el, binding, eventName){
        var me = this;

        Ext.apply(me, {
            target: Ext.get(el),
            eventName: eventName || me.eventName,
            bindings: []
        });
        if (binding) {
            me.addBinding(binding);
        }
        me.enable();
    },

    
    addBinding : function(binding){
        var me = this,
            keyCode = binding.key,
            i,
            len;

        if (me.processing) {
            me.bindings = bindings.slice(0);
        }
        
        if (Ext.isArray(binding)) {
            for (i = 0, len = binding.length; i < len; i++) {
                me.addBinding(binding[i]);
            }
            return;
        }

        me.bindings.push(Ext.apply({
            keyCode: me.processKeys(keyCode)
        }, binding));
    },
    
    
    removeBinding: function(binding){
        var me = this,
            bindings = me.bindings,
            len = bindings.length,
            i, item, keys;
            
        if (me.processing) {
            me.bindings = bindings.slice(0);
        }
        
        keys = me.processKeys(binding.key);
        for (i = 0; i < len; ++i) {
            item = bindings[i];
            if (item.fn === binding.fn && item.scope === binding.scope) {
                if (binding.alt == item.alt && binding.crtl == item.crtl && binding.shift == item.shift) {
                    if (Ext.Array.equals(item.keyCode, keys)) {
                        Ext.Array.erase(me.bindings, i, 1);
                        return;
                    }
                }
            }
        }
    },
    
    processKeys: function(keyCode){
        var processed = false,
            key, keys, keyString, len, i;
            
        if (Ext.isString(keyCode)) {
            keys = [];
            keyString = keyCode.toUpperCase();

            for (i = 0, len = keyString.length; i < len; ++i){
                keys.push(keyString.charCodeAt(i));
            }
            keyCode = keys;
            processed = true;
        }

        if (!Ext.isArray(keyCode)) {
            keyCode = [keyCode];
        }

        if (!processed) {
            for (i = 0, len = keyCode.length; i < len; ++i) {
                key = keyCode[i];
                if (Ext.isString(key)) {
                    keyCode[i] = key.toUpperCase().charCodeAt(0);
                }
            }
        }
        return keyCode;
    },

    
    handleTargetEvent: (function() {
        var tagRe = /input|textarea/i;

        return function(event) {
            var me = this,
                bindings, i, len,
                target, contentEditable;

            if (me.enabled) { 
                bindings = me.bindings;
                i = 0;
                len = bindings.length;

                
                event = me.processEvent.apply(me||me.processEventScope, arguments);

                
                if (me.ignoreInputFields) {
                    target = event.target;
                    contentEditable = target.contentEditable;
                    
                    
                    
                    if (tagRe.test(target.tagName) || (contentEditable === '' || contentEditable === 'true')) {
                        return;
                    }
                }

                
                
                if (!event.getKey) {
                    return event;
                }
                me.processing = true;
                for(; i < len; ++i){
                    me.processBinding(bindings[i], event);
                }
                me.processing = false;
            }
        }
    }()),

    
    processEvent: Ext.identityFn,

    
    processBinding: function(binding, event){
        if (this.checkModifiers(binding, event)) {
            var key = event.getKey(),
                handler = binding.fn || binding.handler,
                scope = binding.scope || this,
                keyCode = binding.keyCode,
                defaultEventAction = binding.defaultEventAction,
                i,
                len,
                keydownEvent = new Ext.EventObjectImpl(event);


            for (i = 0, len = keyCode.length; i < len; ++i) {
                if (key === keyCode[i]) {
                    if (handler.call(scope, key, event) !== true && defaultEventAction) {
                        keydownEvent[defaultEventAction]();
                    }
                    break;
                }
            }
        }
    },

    
    checkModifiers: function(binding, e) {
        var keys = ['shift', 'ctrl', 'alt'],
            i = 0,
            len = keys.length,
            val, key;

        for (; i < len; ++i){
            key = keys[i];
            val = binding[key];
            if (!(val === undefined || (val === e[key + 'Key']))) {
                return false;
            }
        }
        return true;
    },

    
    on: function(key, fn, scope) {
        var keyCode, shift, ctrl, alt;
        if (Ext.isObject(key) && !Ext.isArray(key)) {
            keyCode = key.key;
            shift = key.shift;
            ctrl = key.ctrl;
            alt = key.alt;
        } else {
            keyCode = key;
        }
        this.addBinding({
            key: keyCode,
            shift: shift,
            ctrl: ctrl,
            alt: alt,
            fn: fn,
            scope: scope
        });
    },
    
    
    un: function(key, fn, scope) {
        var keyCode, shift, ctrl, alt;
        if (Ext.isObject(key) && !Ext.isArray(key)) {
            keyCode = key.key;
            shift = key.shift;
            ctrl = key.ctrl;
            alt = key.alt;
        } else {
            keyCode = key;
        }
        this.removeBinding({
            key: keyCode,
            shift: shift,
            ctrl: ctrl,
            alt: alt,
            fn: fn,
            scope: scope
        });
    },

    
    isEnabled : function() {
        return this.enabled;
    },

    
    enable: function() {
        var me = this;
        
        if (!me.enabled) {
            me.target.on(me.eventName, me.handleTargetEvent, me);
            me.enabled = true;
        }
    },

    
    disable: function() {
        var me = this;
        
        if (me.enabled) {
            me.target.removeListener(me.eventName, me.handleTargetEvent, me);
            me.enabled = false;
        }
    },

    
    setDisabled : function(disabled) {
        if (disabled) {
            this.disable();
        } else {
            this.enable();
        }
    },

    
    destroy: function(removeTarget) {
        var me = this,
            target = me.target;

        me.bindings = [];
        me.disable();
        if (removeTarget === true) {
            if (target.isComponent) {
                target.destroy();
            } else {
                target.remove();
            }
        }
        delete me.target;
    }
});


Ext.define('Ext.util.KeyNav', {
    alternateClassName: 'Ext.KeyNav',

                                  

    statics: {
        keyOptions: {
            left: 37,
            right: 39,
            up: 38,
            down: 40,
            space: 32,
            pageUp: 33,
            pageDown: 34,
            del: 46,
            backspace: 8,
            home: 36,
            end: 35,
            enter: 13,
            esc: 27,
            tab: 9
        }
    },

    constructor: function(config) {
        var me = this;
        if (arguments.length === 2) {
            me.legacyConstructor.apply(me, arguments);
            return;
        }
        me.setConfig(config);
    },

    
    legacyConstructor: function(el, config) {
        this.setConfig(Ext.apply({
            target: el
        }, config));
    },

    
    setConfig: function(config) {
        var me = this,
            keymapCfg = {
                target: config.target,
                ignoreInputFields: config.ignoreInputFields,
                eventName: me.getKeyEvent('forceKeyDown' in config ? config.forceKeyDown : me.forceKeyDown, config.eventName)
            },
            map, keyCodes, defaultScope, keyName, binding;

        if (me.map) {
            me.map.destroy();
        }

        if (config.processEvent) {
            keymapCfg.processEvent = config.processEvent;
            keymapCfg.processEventScope = config.processEventScope||me;
        }

        
        if (config.keyMap) {
            map = me.map = config.keyMap;
        }
        
        else {
            map = me.map = new Ext.util.KeyMap(keymapCfg);
            me.destroyKeyMap = true;
        }
        keyCodes = Ext.util.KeyNav.keyOptions;
        defaultScope = config.scope || me;

        for (keyName in keyCodes) {
            if (keyCodes.hasOwnProperty(keyName)) {

                
                
                if (binding = config[keyName]) {
                    if (typeof binding === 'function') {
                        binding = {
                            handler: binding,
                            defaultEventAction: (config.defaultEventAction !== undefined) ? config.defaultEventAction : me.defaultEventAction
                        };
                    }
                    map.addBinding({
                        key: keyCodes[keyName],
                        handler: Ext.Function.bind(me.handleEvent, binding.scope||defaultScope, binding.handler||binding.fn, true),
                        defaultEventAction: (binding.defaultEventAction !== undefined) ? binding.defaultEventAction : me.defaultEventAction
                    });
                }
            }
        }

        map.disable();
        if (!config.disabled) {
            map.enable();
        }
    },

    
    handleEvent: function(keyCode, event, handler){
        return handler.call(this, event);
    },

    
    disabled: false,

    
    defaultEventAction: "stopEvent",

    
    forceKeyDown: false,

    

    
    eventName: 'keypress',

    

    

    

    

    
    destroy: function(removeEl) {
        if (this.destroyKeyMap) {
            this.map.destroy(removeEl);
        }
        delete this.map;
    },

    
    enable: function() {
        
        if (this.map) {
            this.map.enable();
            this.disabled = false;
        }
    },

    
    disable: function() {
        
        if (this.map) {
            this.map.disable();
        }
        this.disabled = true;
    },

    
    setDisabled : function(disabled) {
        this.map.setDisabled(disabled);
        this.disabled = disabled;
    },

    
    getKeyEvent: function(forceKeyDown, configuredEventName) {
        if (forceKeyDown || (Ext.EventManager.useKeyDown && !configuredEventName)) {
            return 'keydown';
        } else {
            return configuredEventName||this.eventName;
        }
    }
});


Ext.define('Ext.FocusManager', {
    singleton: true,
    alternateClassName: ['Ext.FocusMgr' ],

    mixins: {
        observable:  Ext.util.Observable 
    },

               
                                
                        
                               
                             
                           
                         
      

    
    enabled: false,

    

    focusElementCls: Ext.baseCSSPrefix + 'focus-element',

    focusFrameCls: Ext.baseCSSPrefix + 'focus-frame',

    
    whitelist: [
        'textfield'
    ],

    constructor: function(config) {
        var me = this,
            CQ = Ext.ComponentQuery;

        me.mixins.observable.constructor.call(me, config);

        me.addEvents(
            
            'beforecomponentfocus',

            
            'componentfocus',

            
            'disable',

            
            'enable'
        );

        me.focusTask = new Ext.util.DelayedTask(me.handleComponentFocus, me);

        
        Ext.override(Ext.AbstractComponent, {
            onFocus: function() {
                this.callParent(arguments);
                if (me.enabled && this.hasFocus) {
                    Array.prototype.unshift.call(arguments, this);
                    me.onComponentFocus.apply(me, arguments);
                }
            },
            onBlur: function() {
                this.callParent(arguments);
                if (me.enabled && !this.hasFocus) {
                    Array.prototype.unshift.call(arguments, this);
                    me.onComponentBlur.apply(me, arguments);
                }
            },
            onDestroy: function() {
                this.callParent(arguments);
                if (me.enabled) {
                    Array.prototype.unshift.call(arguments, this);
                    me.onComponentDestroy.apply(me, arguments);
                }
            }
        });
        Ext.override(Ext.Component, {
            afterHide: function() {
                this.callParent(arguments);
                if (me.enabled) {
                    Array.prototype.unshift.call(arguments, this);
                    me.onComponentHide.apply(me, arguments);
                }
            }
        });
        
        
        me.keyNav = new Ext.util.KeyNav(Ext.getDoc(), {
            disabled: true,
            scope: me,

            backspace: me.focusLast,
            enter: me.navigateIn,
            esc: me.navigateOut,
            tab: me.navigateSiblings,
            space: me.navigateIn,
            del: me.focusLast,
            left: me.navigateSiblings,
            right: me.navigateSiblings,
            down: me.navigateSiblings,
            up: me.navigateSiblings
        });

        me.focusData = {};
        me.subscribers = new Ext.util.HashMap();
        me.focusChain = {};

        
        Ext.apply(CQ.pseudos, {
            
            nextFocus: function(cmps, idx, step) {
                step = step || 1;
                idx = parseInt(idx, 10);

                var len = cmps.length,
                    i = idx, c;

                for (;;) {
                    
                    if ((i += step) >= len) {
                        i = 0;
                    } else if (i < 0) {
                        i = len - 1;
                    }

                    
                    if (i === idx) {
                        return [];
                    }

                    
                    if ((c = cmps[i]).isFocusable()) {
                        return [c];
                    }
                }

                return [];
            },

            prevFocus: function(cmps, idx) {
                return this.nextFocus(cmps, idx, -1);
            },

            root: function(cmps) {
                var len = cmps.length,
                    results = [],
                    i = 0,
                    c;

                for (; i < len; i++) {
                    c = cmps[i];
                    if (!c.ownerCt) {
                        results.push(c);
                    }
                }

                return results;
            }
        });
    },

    
    addXTypeToWhitelist: function(xtype) {
        var me = this;

        if (Ext.isArray(xtype)) {
            Ext.Array.forEach(xtype, me.addXTypeToWhitelist, me);
            return;
        }

        if (!Ext.Array.contains(me.whitelist, xtype)) {
            me.whitelist.push(xtype);
        }
    },

    clearComponent: function(cmp) {
        clearTimeout(this.cmpFocusDelay);
        if (!cmp.isDestroyed) {
            cmp.blur();
        }
    },

    
    disable: function() {
        var me = this;

        if (!me.enabled) {
            return;
        }

        delete me.options;
        me.enabled = false;

        me.removeDOM();

        
        me.keyNav.disable();

        me.fireEvent('disable', me);
    },

    
    enable: function(options) {
        var me = this;

        if (options === true) {
            options = { focusFrame: true };
        }
        me.options = options = options || {};

        if (me.enabled) {
            return;
        }

        
        me.enabled = true;
        me.initDOM(options);

        
        me.keyNav.enable();

        
        me.focusEl.focus();
        delete me.focusedCmp;

        me.fireEvent('enable', me);
    },

    focusLast: function(e) {
        var me = this;

        if (me.isWhitelisted(me.focusedCmp)) {
            return true;
        }

        
        if (me.previousFocusedCmp) {
            me.previousFocusedCmp.focus();
        }
    },

    getRootComponents: function() {
        var CQ = Ext.ComponentQuery,
            inline = CQ.query(':focusable:root:not([floating])'),
            floating = CQ.query(':focusable:root[floating]');

        
        
        floating.sort(function(a, b) {
            return a.el.getZIndex() > b.el.getZIndex();
        });

        return floating.concat(inline);
    },

    initDOM: function(options) {
        var me = this,
            cls = me.focusFrameCls,
            needListeners = Ext.ComponentQuery.query('{getFocusEl()}:not([focusListenerAdded])'),
            i = 0, len = needListeners.length;

        if (!Ext.isReady) {
            return Ext.onReady(me.initDOM, me);
        }

        
        
        
        for (; i < len; i++) {
            needListeners[i].addFocusListener();
        }

        
        if (!me.focusEl) {
            me.focusEl = Ext.getBody();
            me.focusEl.dom.tabIndex = -1;
        }

        
        if (!me.focusFrame && options.focusFrame) {
            me.focusFrame = Ext.getBody().createChild({
                cls: cls,
                children: [
                    { cls: cls + '-top' },
                    { cls: cls + '-bottom' },
                    { cls: cls + '-left' },
                    { cls: cls + '-right' }
                ],
                style: 'top: -100px; left: -100px;'
            });
            me.focusFrame.setVisibilityMode(Ext.Element.DISPLAY);
            me.focusFrame.hide().setLocalXY(0, 0);
        }
    },

    isWhitelisted: function(cmp) {
        return cmp && Ext.Array.some(this.whitelist, function(x) {
            return cmp.isXType(x);
        });
    },

    navigateIn: function(e) {
        var me = this,
            focusedCmp = me.focusedCmp,
            defaultRoot,
            firstChild;

        if (me.isWhitelisted(focusedCmp)) {
            return true;
        }

        if (!focusedCmp) {
            
            defaultRoot = me.getRootComponents()[0];
            if (defaultRoot) {
                
                
                if (defaultRoot.getFocusEl() === me.focusEl) {
                    me.focusEl.blur();
                }
                defaultRoot.focus();
            }
        } else {
            
            
            firstChild = focusedCmp.hasFocus ? Ext.ComponentQuery.query('>:focusable', focusedCmp)[0] : focusedCmp;
            if (firstChild) {
                firstChild.focus();
            } else {
                
                if (Ext.isFunction(focusedCmp.onClick)) {
                    e.button = 0;
                    focusedCmp.onClick(e);
                    if (focusedCmp.isVisible(true)) {
                        focusedCmp.focus();
                    } else {
                        me.navigateOut();
                    }
                }
            }
        }
    },

    navigateOut: function(e) {
        var me = this,
            parent;

        if (!me.focusedCmp || !(parent = me.focusedCmp.up(':focusable'))) {
            me.focusEl.focus();
        } else {
            parent.focus();
        }

        
        
        
        return true;
    },

    navigateSiblings: function(e, source, parent) {
        var me = this,
            src = source || me,
            key = e.getKey(),
            EO = Ext.EventObject,
            goBack = e.shiftKey || key == EO.LEFT || key == EO.UP,
            checkWhitelist = key == EO.LEFT || key == EO.RIGHT || key == EO.UP || key == EO.DOWN,
            nextSelector = goBack ? 'prev' : 'next',
            idx, next, focusedCmp, siblings;

        focusedCmp = (src.focusedCmp && src.focusedCmp.comp) || src.focusedCmp;
        if (!focusedCmp && !parent) {
            return true;
        }

        if (checkWhitelist && me.isWhitelisted(focusedCmp)) {
            return true;
        }

        
        if (!focusedCmp || focusedCmp.is(':root')) {
            siblings = me.getRootComponents();
        } else {
            
            parent = parent || focusedCmp.up();
            if (parent) {
                siblings = parent.getRefItems();
            }
        }


        
        if (siblings) {
            idx = focusedCmp ? Ext.Array.indexOf(siblings, focusedCmp) : -1;
            next = Ext.ComponentQuery.query(':' + nextSelector + 'Focus(' + idx + ')', siblings)[0];
            if (next && focusedCmp !== next) {
                next.focus();
                return next;
            }
        }
    },

    onComponentBlur: function(cmp, e) {
        var me = this;

        if (me.focusedCmp === cmp) {
            me.previousFocusedCmp = cmp;
            delete me.focusedCmp;
        }

        if (me.focusFrame) {
            me.focusFrame.hide();
        }
    },

    onComponentFocus: function(cmp, e) {
        var me = this,
            chain = me.focusChain,
            parent;

        if (!cmp.isFocusable()) {
            me.clearComponent(cmp);

            
            
            
            
            if (chain[cmp.id]) {
                return;
            }

            
            parent = cmp.up();
            if (parent) {
                
                
                
                chain[cmp.id] = true;
                parent.focus();
            }

            return;
        }
        
        me.focusChain = {};

        
        
        
        me.focusTask.delay(10, null, null, [cmp, cmp.getFocusEl()]);
    },

    handleComponentFocus: function(cmp, focusEl) {
        var me = this,
            cls,
            ff,
            box,
            bt,
            bl,
            bw,
            bh,
            ft,
            fb,
            fl,
            fr;

        if (me.fireEvent('beforecomponentfocus', me, cmp, me.previousFocusedCmp) === false) {
            me.clearComponent(cmp);
            return;
        }

        me.focusedCmp = cmp;

        
        if (me.shouldShowFocusFrame(cmp)) {
            cls = '.' + me.focusFrameCls + '-';
            ff = me.focusFrame;
            
            
            box = (focusEl.dom ? focusEl : focusEl.el).getBox();

            
            
            
            bt = box.top;
            bl = box.left;
            bw = box.width;
            bh = box.height;
            ft = ff.child(cls + 'top');
            fb = ff.child(cls + 'bottom');
            fl = ff.child(cls + 'left');
            fr = ff.child(cls + 'right');

            ft.setWidth(bw).setLocalXY(bl, bt);
            fb.setWidth(bw).setLocalXY(bl, bt + bh - 2);
            fl.setHeight(bh - 2).setLocalXY(bl, bt + 2);
            fr.setHeight(bh - 2).setLocalXY(bl + bw - 2, bt + 2);

            ff.show();
        }

        me.fireEvent('componentfocus', me, cmp, me.previousFocusedCmp);
    },

    onComponentHide: function(cmp) {
        var me = this,
            cmpHadFocus = false,
            focusedCmp = me.focusedCmp,
            parent;

        if (focusedCmp) {
            
            
            cmpHadFocus = cmp.hasFocus || (cmp.isContainer && cmp.isAncestor(me.focusedCmp));
        }

        me.clearComponent(cmp);

        
        if (cmpHadFocus && (parent = cmp.up(':focusable'))) {
            parent.focus();
        } else {
            me.focusEl.focus();
        }
    },

    onComponentDestroy: function() {

    },

    removeDOM: function() {
        var me = this;

        
        
        if (me.enabled || me.subscribers.length) {
            return;
        }

        Ext.destroy(
            me.focusFrame
        );
        delete me.focusEl;
        delete me.focusFrame;
    },

    
    removeXTypeFromWhitelist: function(xtype) {
        var me = this;

        if (Ext.isArray(xtype)) {
            Ext.Array.forEach(xtype, me.removeXTypeFromWhitelist, me);
            return;
        }

        Ext.Array.remove(me.whitelist, xtype);
    },

    setupSubscriberKeys: function(container, keys) {
        var me = this,
            el = container.getFocusEl(),
            scope = keys.scope,
            handlers = {
                backspace: me.focusLast,
                enter: me.navigateIn,
                esc: me.navigateOut,
                scope: me
            },

            navSiblings = function(e) {
                if (me.focusedCmp === container) {
                    
                    
                    
                    return me.navigateSiblings(e, me, container);
                } else {
                    return me.navigateSiblings(e);
                }
            };

        Ext.iterate(keys, function(key, cb) {
            handlers[key] = function(e) {
                var ret = navSiblings(e);

                if (Ext.isFunction(cb) && cb.call(scope || container, e, ret) === true) {
                    return true;
                }

                return ret;
            };
        }, me);

        return new Ext.util.KeyNav(el, handlers);
    },

    shouldShowFocusFrame: function(cmp) {
        var me = this,
            opts = me.options || {};

        
        
        
        if (!me.focusFrame || !cmp) {
            return false;
        }

        
        if (opts.focusFrame) {
            return true;
        }

        if (me.focusData[cmp.id].focusFrame) {
            return true;
        }

        return false;
    }
});


Ext.define('Ext.Img', {
    extend:  Ext.Component ,
    alias: ['widget.image', 'widget.imagecomponent'],

    autoEl: 'img',

    baseCls: Ext.baseCSSPrefix + 'img',

    
    src: '',

    
    alt: '',

    
    title: '',

    
    imgCls: '',

    

    ariaRole: 'img',

    initComponent: function() {
        if (this.glyph) {
            this.autoEl = 'div';
        }
        this.callParent();
    },

    getElConfig: function() {
        var me = this,
            autoEl = me.autoEl,
            config = me.callParent(),
            glyphFontFamily = Ext._glyphFontFamily,
            glyph = me.glyph,
            img, glyphParts;

        
        
        if (autoEl === 'img' || (Ext.isObject(autoEl) && autoEl.tag === 'img')) {
            img = config;
        } else if (me.glyph) {
            if (typeof glyph === 'string') {
                glyphParts = glyph.split('@');
                glyph = glyphParts[0];
                glyphFontFamily = glyphParts[1];
            }
            config.html = '&#' + glyph + ';';
            if (glyphFontFamily) {
                config.style = 'font-family:' + glyphFontFamily;
            }
        } else {
            config.cn = [img = {
                tag: 'img',
                role: me.ariaRole,
                id: me.id + '-img'
            }];
        }

        if (img) {
            if (me.imgCls) {
                img.cls = (img.cls ? img.cls + ' ' : '') + me.imgCls;
            }

            img.src = me.src || Ext.BLANK_IMAGE_URL;
        }

        if (me.alt) {
            (img || config).alt = me.alt;
        }
        if (me.title) {
            (img || config).title = me.title;
        }

        return config;
    },

    onRender: function () {
        var me = this,
            autoEl = me.autoEl,
            el;

        me.callParent(arguments);

        el = me.el;
        
        if (autoEl === 'img' || (Ext.isObject(autoEl) && autoEl.tag === 'img')) {
            me.imgEl = el;
        }
        else {
            me.imgEl = el.getById(me.id + '-img');
        }
    },

    onDestroy: function () {
        Ext.destroy(this.imgEl);
        this.imgEl = null;
        this.callParent();
    },

    
    setSrc: function(src) {
        var me = this,
            imgEl = me.imgEl;

        me.src = src;

        if (imgEl) {
            imgEl.dom.src = src || Ext.BLANK_IMAGE_URL;
        }
    },

    setGlyph: function(glyph) {
        var me = this,
            glyphFontFamily = Ext._glyphFontFamily,
            glyphParts, dom;

        if (glyph != me.glyph) {
            if (typeof glyph === 'string') {
                glyphParts = glyph.split('@');
                glyph = glyphParts[0];
                glyphFontFamily = glyphParts[1];
            }

            dom = me.el.dom;

            dom.innerHTML = '&#' + glyph + ';';
            if (glyphFontFamily) {
                dom.style = 'font-family:' + glyphFontFamily;
            }
        }
    }
});


Ext.define('Ext.util.Bindable', {

    
    bindStore: function(store, initial, propertyName) {
        
        
        
        propertyName = propertyName || 'store';

        var me = this,
            oldStore = me[propertyName];

        if (!initial && oldStore) {
            
            me.onUnbindStore(oldStore, initial, propertyName);

            if (store !== oldStore && oldStore.autoDestroy) {
                oldStore.destroyStore();
            } else {
                me.unbindStoreListeners(oldStore);
            }
        }
        if (store) {
            store = Ext.data.StoreManager.lookup(store);
            me.bindStoreListeners(store);
            me.onBindStore(store, initial, propertyName);
        }
        me[propertyName] = store || null;
        return me;
    },

    
    getStore: function(){
        return this.store;
    },

    
    unbindStoreListeners: function(store) {
        
        var listeners = this.storeListeners;
        if (listeners) {
            store.un(listeners);
        }
    },

    
    bindStoreListeners: function(store) {
        
        var me = this,
            listeners = Ext.apply({}, me.getStoreListeners(store));

        if (!listeners.scope) {
            listeners.scope = me;
        }
        me.storeListeners = listeners;
        store.on(listeners);
    },

    
    getStoreListeners: Ext.emptyFn,

    
    onUnbindStore: Ext.emptyFn,

    
    onBindStore: Ext.emptyFn    
});


Ext.define('Ext.LoadMask', {

    extend:  Ext.Component ,

    alias: 'widget.loadmask',

    

    mixins: {
        floating:  Ext.util.Floating ,
        bindable:  Ext.util.Bindable 
    },

                                    

    
    
    

    

    
    
    msg : 'Loading...',
    

    
    msgCls : Ext.baseCSSPrefix + 'mask-loading',

    
    maskCls: Ext.baseCSSPrefix + 'mask',

    
    useMsg: true,

    
    useTargetEl: false,

    baseCls: Ext.baseCSSPrefix + 'mask-msg',
    
    ariaRole: 'presentation',

    childEls: [
        'msgEl',
        'msgTextEl'
    ],

    renderTpl: [
        '<div id="{id}-msgEl" role="{role}"',
            '<tpl if="ariaAttr"> {ariaAttr}</tpl>',
            ' class="{[values.$comp.msgCls]} ',
            Ext.baseCSSPrefix, 'mask-msg-inner{childElCls}">',
            '<div id="{id}-msgTextEl" class="', Ext.baseCSSPrefix ,'mask-msg-text',
                '{childElCls}"></div>',
        '</div>'
    ],

    
    floating: {
        shadow: 'frame'
    },

    
    focusOnToFront: false,

    
    
    bringParentToFront: false,

    
    constructor : function(config) {
        var me = this,
            comp;

        if (arguments.length === 2) {
            comp = config;
            config = arguments[1];
        } else {
            comp = config.target;
        }

        
        if (!comp.isComponent) {
            comp = Ext.get(comp);
            this.isElement = true;
        }

        me.ownerCt = comp;
        if (!this.isElement) {
            me.bindComponent(comp);
        }
        me.callParent([config]);

        if (me.store) {
            me.bindStore(me.store, true);
        }
    },

    bindComponent: function(comp) {
        var me = this,
            listeners = {
                scope: this,
                resize: me.sizeMask,
                added: me.onComponentAdded,
                removed: me.onComponentRemoved
            };
            
        if (comp.floating) {
            listeners.move = me.sizeMask;
            me.activeOwner = comp;
        } else if (comp.ownerCt) {
            me.onComponentAdded(comp.ownerCt);
        } else {
            
            me.preventBringToFront = true;
        }

        me.mon(comp, listeners);
        
        
        me.mon(me.hierarchyEventSource, {
            show: me.onContainerShow,
            hide: me.onContainerHide,
            expand: me.onContainerExpand,
            collapse: me.onContainerCollapse,
            scope: me
        });
    },

    onComponentAdded: function(owner) {
        var me = this;
        delete me.activeOwner;
        me.floatParent = owner;
        if (!owner.floating) {
            owner = owner.up('[floating]');
        }
        if (owner) {
            me.activeOwner = owner;
            me.mon(owner, 'move', me.sizeMask, me);
        } else {
            me.preventBringToFront = true;
        }
        owner = me.floatParent.ownerCt;
        if (me.rendered && me.isVisible() && owner) {
            me.floatOwner = owner;
            me.mon(owner, 'afterlayout', me.sizeMask, me, {single: true});
        }
    },

    onComponentRemoved: function(owner) {
        var me = this,
            activeOwner = me.activeOwner,
            floatOwner = me.floatOwner;

        if (activeOwner) {
            me.mun(activeOwner, 'move', me.sizeMask, me);
        }
        if (floatOwner) {
            me.mun(floatOwner, 'afterlayout', me.sizeMask, me);
        }
        delete me.activeOwner;
        delete me.floatOwner;
    },

    afterRender: function() {
        this.callParent(arguments);
            this.container = this.floatParent.getContentTarget();
    },

    onContainerShow: function(container) {
        if (this.isActiveContainer(container)) {
            this.onComponentShow();
        }
    },

    onContainerHide: function(container) {
        if (this.isActiveContainer(container)) {
            this.onComponentHide();
        }
    },

    onContainerExpand: function(container) {
        if (this.isActiveContainer(container)) {
            this.onComponentShow();
        }
    },

    onContainerCollapse: function(container) {
        if (this.isActiveContainer(container)) {
            this.onComponentHide();
        }
    },

    isActiveContainer: function(container) {
        return this.isDescendantOf(container);
    },

    onComponentHide: function() {
        var me = this;

        if (me.rendered && me.isVisible()) {
            me.hide();
            me.showNext = true;
        }
    },

    onComponentShow: function() {
        if (this.showNext) {
            this.show();
        }
        delete this.showNext;
    },

    
    sizeMask: function() {
        var me = this,
            target;

        if (me.rendered && me.isVisible()) {
            me.center();

            target = me.getMaskTarget();
            me.getMaskEl().show().setSize(target.getSize()).alignTo(target, 'tl-tl');

        }
    },

    
    bindStore : function(store, initial) {
        var me = this;
        me.mixins.bindable.bindStore.apply(me, arguments);
        store = me.store;
        if (store && store.isLoading()) {
            me.onBeforeLoad();
        }
    },

    getStoreListeners: function(store) {
        var load = this.onLoad,
            beforeLoad = this.onBeforeLoad,
            result = {
                
                cachemiss: beforeLoad,

                
                cachefilled: load
            };

        
        if (!store.proxy.isSynchronous) {
            result.beforeLoad = beforeLoad;
            result.load = load;
        }
        return result;
    },

    onDisable : function() {
        this.callParent(arguments);
        if (this.loading) {
            this.onLoad();
        }
    },

    getOwner: function() {
        return this.ownerCt || this.floatParent;
    },

    getMaskTarget: function() {
        var owner = this.getOwner();
        return this.useTargetEl ? owner.getTargetEl() : owner.getEl();
    },

    
    onBeforeLoad : function() {
        var me = this,
            owner = me.getOwner(),
            origin;

        if (!me.disabled) {
            me.loading = true;
            
            
            if (owner.componentLayoutCounter) {
                me.maybeShow();
            } else {
                
                origin = owner.afterComponentLayout;
                owner.afterComponentLayout = function() {
                    owner.afterComponentLayout = origin;
                    origin.apply(owner, arguments);
                    me.maybeShow();
                };
            }
        }
    },

    maybeShow: function() {
        var me = this,
            owner = me.getOwner();

        if (!owner.isVisible(true)) {
            me.showNext = true;
        }
        else if (me.loading && owner.rendered) {
            me.show();
        }
    },

    getMaskEl: function(){
        var me = this;
        return me.maskEl || (me.maskEl = me.el.insertSibling({
            role: 'presentation',
            cls: me.maskCls,
            style: {
                zIndex: me.el.getStyle('zIndex') - 2
            }
        }, 'before'));
    },

    onShow: function() {
        var me = this,
            msgEl = me.msgEl;

        me.callParent(arguments);
        me.loading = true;

        if (me.useMsg) {
            msgEl.show();
            me.msgTextEl.update(me.msg);
        } else {
            msgEl.parent().hide();
        }
    },

    hide: function() {
        
        if (this.isElement) {
            this.ownerCt.unmask();
            this.fireEvent('hide', this);
            return;
        }
        delete this.showNext;
        return this.callParent(arguments);
    },

    onHide: function() {
        this.callParent();
        this.getMaskEl().hide();
    },

    show: function() {
        
        if (this.isElement) {
            this.ownerCt.mask(this.useMsg ? this.msg : '', this.msgCls);
            this.fireEvent('show', this);
            return;
        }
        return this.callParent(arguments);
    },

    afterShow: function() {
        this.callParent(arguments);
        this.sizeMask();
    },

    setZIndex: function(index) {
        var me = this,
            owner = me.activeOwner;
            
        if (owner) {
            
            
            
            index = parseInt(owner.el.getStyle('zIndex'), 10) + 1;
        }

        me.getMaskEl().setStyle('zIndex', index - 1);
        return me.mixins.floating.setZIndex.apply(me, arguments);
    },

    
    onLoad : function() {
        this.loading = false;
        this.hide();
    },

    onDestroy: function() {
        var me = this;

        if (me.isElement) {
            me.ownerCt.unmask();
        }

        Ext.destroy(me.maskEl);
        me.callParent();
    }
});


Ext.define('Ext.data.association.Association', {
    alternateClassName: 'Ext.data.Association',
    

    

    

    
    primaryKey: 'id',

    
    
    

    associationKeyFunction : null,

    defaultReaderType: 'json',

    isAssociation: true,

    initialConfig: null,

    statics: {
        AUTO_ID: 1000,
        
        create: function(association){
            if (Ext.isString(association)) {
                association = {
                    type: association
                };
            }

            switch (association.type) {
                case 'belongsTo':
                    return new Ext.data.association.BelongsTo(association);
                case 'hasMany':
                    return new Ext.data.association.HasMany(association);
                case 'hasOne':
                    return new Ext.data.association.HasOne(association);
                


                default:
            }
            return association;
        }
    },

    
    constructor: function(config) {
        Ext.apply(this, config);

        var me              = this,
            types           = Ext.ModelManager.types,
            ownerName       = config.ownerModel,
            associatedName  = config.associatedModel,
            ownerModel      = types[ownerName],
            associatedModel = types[associatedName],
            associationKey  = config.associationKey,
            keyReIdx;

        if (associationKey) {
            keyReIdx = String(associationKey).search(/[\[\.]/);

            if (keyReIdx >= 0) {
                me.associationKeyFunction = Ext.functionFactory('obj', 'return obj' + (keyReIdx > 0 ? '.' : '') + associationKey);
            }
        }

        me.initialConfig = config;


        me.ownerModel = ownerModel;
        me.associatedModel = associatedModel;

        

        

        Ext.applyIf(me, {
            ownerName : ownerName,
            associatedName: associatedName
        });
        
        me.associationId = 'association' + (++me.statics().AUTO_ID);
    },

    
    getReader: function(){
        var me = this,
            reader = me.reader,
            model = me.associatedModel;

        if (reader) {
            if (Ext.isString(reader)) {
                reader = {
                    type: reader
                };
            }
            if (reader.isReader) {
                reader.setModel(model);
            } else {
                Ext.applyIf(reader, {
                    model: model,
                    type : me.defaultReaderType
                });
            }
            me.reader = Ext.createByAlias('reader.' + reader.type, reader);
        }
        return me.reader || null;
    }
});


Ext.define('Ext.ModelManager', {
    extend:  Ext.AbstractManager ,
    alternateClassName: 'Ext.ModelMgr',
                                                   
    
    singleton: true,

    typeName: 'mtype',

    
    associationStack: [],

    
    registerType: function(name, config) {
        var proto = config.prototype,
            model;
        if (proto && proto.isModel) {
            
            model = config;
        } else {
            
            if (!config.extend) {
                config.extend = 'Ext.data.Model';
            }
            model = Ext.define(name, config);
        }
        this.types[name] = model;
        return model;
    },
    
    
    unregisterType: function(name) {
        delete this.types[name];
    },

    
    onModelDefined: function(model) {
        var stack  = this.associationStack,
            length = stack.length,
            create = [],
            association, i, created;

        for (i = 0; i < length; i++) {
            association = stack[i];

            if (association.associatedModel == model.modelName) {
                create.push(association);
            }
        }

        for (i = 0, length = create.length; i < length; i++) {
            created = create[i];
            this.types[created.ownerModel].prototype.associations.add(Ext.data.association.Association.create(created));
            Ext.Array.remove(stack, created);
        }
    },

    
    registerDeferredAssociation: function(association){
        this.associationStack.push(association);
    },

    
    getModel: function(id) {
        var model = id;
        if (typeof model == 'string') {
            model = this.types[model];
        }
        return model;
    },

    
    create: function(config, name, id) {
        var Con = typeof name == 'function' ? name : this.types[name || config.name];

        return new Con(config, id);
    }
}, function() {

    
    Ext.regModel = function() {
        return this.ModelManager.registerType.apply(this.ModelManager, arguments);
    };
});


Ext.define('Ext.layout.component.ProgressBar', {

    

    alias: ['layout.progressbar'],

    extend:  Ext.layout.component.Auto ,

    

    type: 'progressbar',

    beginLayout: function (ownerContext) {
        var me = this,
            i, textEls;

        me.callParent(arguments);

        if (!ownerContext.textEls) {
            textEls = me.owner.textEl; 

            if (textEls.isComposite) {
                ownerContext.textEls = [];
                textEls = textEls.elements;
                for (i = textEls.length; i--; ) {
                    ownerContext.textEls[i] = ownerContext.getEl(Ext.get(textEls[i]));
                }
            } else {
                ownerContext.textEls = [ ownerContext.getEl('textEl') ];
            }
        }
    },

    calculate: function(ownerContext) {
        var me = this,
            i, textEls, width;

        me.callParent(arguments);

        if (Ext.isNumber(width = ownerContext.getProp('width'))) {
            width -= ownerContext.getBorderInfo().width;
            textEls = ownerContext.textEls;

            for (i = textEls.length; i--; ) {
                textEls[i].setWidth(width);
            }
        } else {
            me.done = false;
        }
    }
});


Ext.define('Ext.ProgressBar', {
    extend:  Ext.Component ,
    alias: 'widget.progressbar',

               
                       
                               
                          
                                          
      

                          

   

   

   

   
    baseCls: Ext.baseCSSPrefix + 'progress',

    
    animate: false,

    
    text: '',

    
    waitTimer: null,

    childEls: [
        'bar'
    ],

    renderTpl: [
        '<tpl if="internalText">',
            '<div class="{baseCls}-text {baseCls}-text-back">{text}</div>',
        '</tpl>',
        '<div id="{id}-bar" class="{baseCls}-bar {baseCls}-bar-{ui}" role="presentation" style="width:{percentage}%">',
            '<tpl if="internalText">',
                '<div class="{baseCls}-text">',
                    '<div>{text}</div>',
                '</div>',
            '</tpl>',
        '</div>'
    ],

    componentLayout: 'progressbar',
    
    ariaRole: 'progressbar',

    
    initComponent: function() {
        this.callParent();

        this.addEvents(
            
            "update"
        );
    },

    initRenderData: function() {
        var me = this;
        return Ext.apply(me.callParent(), {
            internalText : !me.hasOwnProperty('textEl'),
            text         : me.text || '&#160;',
            percentage   : me.value ? me.value * 100 : 0
        });
    },

    onRender : function() {
        var me = this;

        me.callParent(arguments);

        
        if (me.textEl) {
            me.textEl = Ext.get(me.textEl);
            me.updateText(me.text);
        }
        
        else {
            
            
            me.textEl = me.el.select('.' + me.baseCls + '-text');
        }
    },

    
    updateProgress: function(value, text, animate) {
        var me = this,
            oldValue = me.value;

        me.value = value || 0;
        if (text) {
            me.updateText(text);
        }
        if (me.rendered && !me.isDestroyed) {
            if (animate === true || (animate !== false && me.animate)) {
                me.bar.stopAnimation();
                me.bar.animate(Ext.apply({
                    from: {
                        width: (oldValue * 100) + '%'
                    },
                    to: {
                        width: (me.value * 100) + '%'
                    }
                }, me.animate));
            } else {
                me.bar.setStyle('width', (me.value * 100) + '%');
            }
        }
        me.fireEvent('update', me, me.value, text);
        return me;
    },

    
    updateText: function(text) {
        var me = this;
        
        me.text = text;
        if (me.rendered) {
            me.textEl.update(me.text);
        }
        return me;
    },

    applyText : function(text) {
        this.updateText(text);
    },
    
    getText: function(){
        return this.text;    
    },

    
    wait: function(o) {
        var me = this, scope;
            
        if (!me.waitTimer) {
            scope = me;
            o = o || {};
            me.updateText(o.text);
            me.waitTimer = Ext.TaskManager.start({
                run: function(i){
                    var inc = o.increment || 10;
                    i -= 1;
                    me.updateProgress(((((i+inc)%inc)+1)*(100/inc))*0.01, null, o.animate);
                },
                interval: o.interval || 1000,
                duration: o.duration,
                onStop: function(){
                    if (o.fn) {
                        o.fn.apply(o.scope || me);
                    }
                    me.reset();
                },
                scope: scope
            });
        }
        return me;
    },

    
    isWaiting: function(){
        return this.waitTimer !== null;
    },

    
    reset: function(hide){
        var me = this;
        
        me.updateProgress(0);
        me.clearTimer();
        if (hide === true) {
            me.hide();
        }
        return me;
    },

    
    clearTimer: function(){
        var me = this;
        
        if (me.waitTimer) {
            me.waitTimer.onStop = null; 
            Ext.TaskManager.stop(me.waitTimer);
            me.waitTimer = null;
        }
    },

    onDestroy: function(){
        var me = this,
            bar = me.bar;
        
        me.clearTimer();
        if (me.rendered) {
            if (me.textEl.isComposite) {
                me.textEl.clear();
            }
            Ext.destroyMembers(me, 'textEl', 'progressBar');
            if (bar && me.animate) {
                bar.stopAnimation();
            }
        }
        me.callParent();
    }
});


Ext.define('Ext.ShadowPool', {
    singleton: true,
                                

    markup: (function() {
        return Ext.String.format(
            '<div class="{0}{1}-shadow" role="presentation"></div>',
            Ext.baseCSSPrefix,
            Ext.isIE && !Ext.supports.CSS3BoxShadow ? 'ie' : 'css'
        );
    }()),

    shadows: [],

    pull: function() {
        var sh = this.shadows.shift();
        if (!sh) {
            sh = Ext.get(Ext.DomHelper.insertHtml("beforeBegin", document.body.firstChild, this.markup));
            sh.autoBoxAdjust = false;
        }
        return sh;
    },

    push: function(sh) {
        this.shadows.push(sh);
    },
    
    reset: function() {
        var shadows = [].concat(this.shadows),
            s,
            sLen    = shadows.length;

        for (s = 0; s < sLen; s++) {
            shadows[s].remove();
        }

        this.shadows = [];
    }
});


Ext.define('Ext.Shadow', {
                                 

    localXYNames: {
        get: 'getLocalXY',
        set: 'setLocalXY'
    },

    
    constructor: function(config) {
        var me = this,
            adjusts,
            offset,
            rad;
        
        Ext.apply(me, config);
        if (!Ext.isString(me.mode)) {
            me.mode = me.defaultMode;
        }
        offset = me.offset;
        rad = Math.floor(offset / 2);
        me.opacity = 50;
        switch (me.mode.toLowerCase()) {
            
            case "drop":
                if (Ext.supports.CSS3BoxShadow) {
                    adjusts = {
                        t: offset,
                        l: offset,
                        h: -offset,
                        w: -offset
                    };
                }
                else {
                    adjusts = {
                        t: -rad,
                        l: -rad,
                        h: -rad,
                        w: -rad
                    };
                }
                break;
            case "sides":
                if (Ext.supports.CSS3BoxShadow) {
                    adjusts = {
                        t: offset,
                        l: 0,
                        h: -offset,
                        w: 0
                    };
                }
                else {
                    adjusts = {
                        t: - (1 + rad),
                        l: 1 + rad - 2 * offset,
                        h: -1,
                        w: rad - 1
                    };
                }
                break;
            case "frame":
                if (Ext.supports.CSS3BoxShadow) {
                    adjusts = {
                        t: 0,
                        l: 0,
                        h: 0,
                        w: 0
                    };
                }
                else {
                    adjusts = {
                        t: 1 + rad - 2 * offset,
                        l: 1 + rad - 2 * offset,
                        h: offset - rad - 1,
                        w: offset - rad - 1
                    };
                }
                break;
            case "bottom":
                if (Ext.supports.CSS3BoxShadow) {
                    adjusts = {
                        t: offset,
                        l: 0,
                        h: -offset,
                        w: 0
                    };
                }
                else {
                    adjusts = {
                        t: offset,
                        l: 0,
                        h: 0,
                        w: 0
                    };
                }
                break;
        }
        me.adjusts = adjusts;
    },

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

    

    
    offset: 4,

    
    defaultMode: "drop",

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

    
    show: function(target) {
        var me = this,
            index, xy;

        target = Ext.get(target);
        
        
        index = (parseInt(target.getStyle("z-index"), 10) - 1) || 0;
        xy = target[me.localXYNames.get]();

        
        if (!me.el) {
            me.el = Ext.ShadowPool.pull();
            
            if (me.fixed) {
                me.el.dom.style.position = 'fixed';
            } else {
                me.el.dom.style.position = '';
            }
            if (me.el.dom.nextSibling != target.dom) {
                me.el.insertBefore(target);
            }
        }
        me.el.setStyle("z-index", me.zIndex || index);
        if (Ext.isIE && !Ext.supports.CSS3BoxShadow) {
            me.el.dom.style.filter = "progid:DXImageTransform.Microsoft.alpha(opacity=" + me.opacity + ") progid:DXImageTransform.Microsoft.Blur(pixelradius=" + (me.offset) + ")";
        }
        me.realign(
            xy[0],
            xy[1],
            target.dom.offsetWidth,
            target.dom.offsetHeight
        );
        me.el.dom.style.display = "block";
    },

    
    isVisible: function() {
        return this.el ? true: false;
    },

    
    realign: function(l, t, targetWidth, targetHeight) {
        if (!this.el) {
            return;
        }
        var adjusts = this.adjusts,
            el = this.el,
            targetStyle = el.dom.style,
            shadowWidth,
            shadowHeight,
            sws,
            shs;

        el[this.localXYNames.set](l + adjusts.l, t + adjusts.t);
        shadowWidth = Math.max(targetWidth + adjusts.w, 0);
        shadowHeight = Math.max(targetHeight + adjusts.h, 0);
        sws = shadowWidth + "px";
        shs = shadowHeight + "px";
        if (targetStyle.width != sws || targetStyle.height != shs) {
            targetStyle.width = sws;
            targetStyle.height = shs;

            if (Ext.supports.CSS3BoxShadow) {
                targetStyle[this.boxShadowProperty] = '0 0 ' + (this.offset + 2) + 'px #888';
            }
        }
    },

    
    hide: function() {
        var me = this;
        
        if (me.el) {
            me.el.dom.style.display = "none";
            Ext.ShadowPool.push(me.el);
            delete me.el;
        }
    },

    
    setZIndex: function(z) {
        this.zIndex = z;
        if (this.el) {
            this.el.setStyle("z-index", z);
        }
    },
    
    
    setOpacity: function(opacity){
        if (this.el) {
            if (Ext.isIE && !Ext.supports.CSS3BoxShadow) {
                opacity = Math.floor(opacity * 100 / 2) / 100;
            }
            this.opacity = opacity;
            this.el.setOpacity(opacity);
        }
    }
});



Ext.define('Ext.app.EventDomain', {
               
                        
      

    statics: {
        
        instances: {}
    },
    
    
     
    isEventDomain: true,

    constructor: function() {
        var me = this;

        Ext.app.EventDomain.instances[me.type] = me;

        me.bus = {};
        me.monitoredClasses = [];
    },

    
    dispatch: function(target, ev, args) {
        var me = this,
            bus = me.bus,
            selectors = bus[ev],
            selector, controllers, id, events, event, i, ln;

        if (!selectors) {
            return true;
        }

        
        for (selector in selectors) {
            
            if (selectors.hasOwnProperty(selector) && me.match(target, selector)) {
                
                controllers = selectors[selector];

                for (id in controllers) {
                    if (controllers.hasOwnProperty(id)) {
                        
                        events = controllers[id];

                        for (i = 0, ln = events.length; i < ln; 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,
            i, tree, list, selector, options, listener, scope, event, listeners, ev;

        for (selector in selectors) {
            if (selectors.hasOwnProperty(selector) && (listeners = selectors[selector])) {
                if (idProperty) {
                
                    selector = selector === '*' ? selector : selector.substring(1);
                }
                
                for (ev in listeners) {
                    if (listeners.hasOwnProperty(ev)) {
                        options  = null;
                        listener = listeners[ev];
                        scope    = controller;
                        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 (typeof listener === 'string') {
                            listener = scope[listener];
                        }
                        event.addListener(listener, scope, options);

                        for (i = monitoredClassesCount; i-- > 0;) {
                            monitoredClasses[i].hasListeners._incr_(ev);
                        }

                        
                        tree = bus[ev]             || (bus[ev] = {});
                        tree = tree[selector]      || (tree[selector] = {});
                        list = tree[controller.id] || (tree[controller.id] = []);

                        
                        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,
            fireEventArgs = prototype.fireEventArgs;

        domain.monitoredClasses.push(observable);

        prototype.fireEventArgs = function(ev, args) {
            var ret = fireEventArgs.apply(this, arguments);

            if (ret !== false) {
                ret = domain.dispatch(this, ev, args);
            }

            return ret;
        };
    },

    
    unlisten: function(controllerId) {
        var bus = this.bus,
            controllers, ev, selector, selectors;

        for (ev in bus) {
            if (bus.hasOwnProperty(ev) && (selectors = bus[ev])) {
                for (selector in selectors) {
                    controllers = selectors[selector];
                    delete controllers[controllerId];  
                }
            }
        }
    }
});


Ext.define('Ext.app.domain.Component', {
    extend:  Ext.app.EventDomain ,
    singleton: true,

               
                       
      

    type: 'component',

    constructor: function() {
        var me = this;
        
        me.callParent();
        me.monitor(Ext.Component);
    },

    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.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) {
        
        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.data.AbstractStore.create(store);
        }
    },

    
    getKey : function(o) {
         return o.storeId;
    }
}, function() {    
    
    Ext.regStore = function(name, config) {
        var store;

        if (Ext.isObject(name)) {
            config = name;
        } else {
            config.storeId = name;
        }

        if (config instanceof Ext.data.Store) {
            store = config;
        } else {
            store = new Ext.data.Store(config);
        }

        return Ext.data.StoreManager.register(store);
    };

    
    Ext.getStore = function(name) {
        return Ext.data.StoreManager.lookup(name);
    };
});


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: function() {
        return true;
    }
});


Ext.define('Ext.data.ResultSet', {
    
    loaded: true,

    
    count: 0,

    
    total: 0,

    
    success: false,

    

    
    constructor: function(config) {
        Ext.apply(this, config);

        
        this.totalRecords = this.total;

        if (config.count === undefined) {
            this.count = this.records.length;
        }
    }
});


Ext.define('Ext.data.reader.Reader', {
                                                      
    alternateClassName: ['Ext.data.Reader', 'Ext.data.DataReader'],

    mixins: {
        observable:  Ext.util.Observable 
    },

    

    
    totalProperty: 'total',

    
    successProperty: 'success',

    
    root: '',
    
    
    
    
    implicitIncludes: true,
    
    
    readRecordsOnFailure: true,
    
    
    
    
    isReader: true,

    
    
    
    applyDefaults: true,

    lastFieldGeneration: null,
    
    
    constructor: function(config) {
        var me = this;
        
        me.mixins.observable.constructor.call(me, config);
        me.model = Ext.ModelManager.getModel(me.model);

        
        
        
        
        
        if (me.model && me.model.prototype.fields) {
            me.buildExtractors();
        }

        this.addEvents(
            
            'exception'
        );
    },

    
    setModel: function(model, setOnProxy) {
        var me = this,
            oldModel = me.model,
            force = true;
        
        model = me.model = Ext.ModelManager.getModel(model);
        if (model && oldModel === model) {
            
            
            
            force = me.lastFieldGeneration !== model.prototype.fields.generation;
        }
        
        if (model) {
            me.buildExtractors(force);
        }
        
        if (setOnProxy && me.proxy) {
            me.proxy.setModel(model, true);
        }
    },

    
    read: function(response) {
        var data;

        if (response) {
            data = response.responseText ? this.getResponseData(response) : this.readRecords(response);
        }

        return data || this.nullResultSet;
    },

    
    readRecords: function(data) {
        var me = this,
            success,
            recordCount,
            records,
            root,
            total,
            value,
            message;
        
        
        if (me.lastFieldGeneration !== me.model.prototype.fields.generation) {
            me.buildExtractors(true);
        }
        
        
        me.rawData = data;

        data = me.getData(data);
        
        success = true;
        recordCount = 0;
        records = [];
            
        if (me.successProperty) {
            value = me.getSuccess(data);
            if (value === false || value === 'false') {
                success = false;
            }
        }
        
        if (me.messageProperty) {
            message = me.getMessage(data);
        }

        
        
        if (me.readRecordsOnFailure || success) {
            
            
            root = Ext.isArray(data) ? data : me.getRoot(data);
            
            if (root) {
                total = root.length;
            }

          if (me.totalProperty) {
                value = parseInt(me.getTotal(data), 10);
                if (!isNaN(value)) {
                    total = value;
                }
            }

           if (root) {
                records = me.extractData(root);
                recordCount = records.length;
            }
        }

        return new Ext.data.ResultSet({
            total  : total || recordCount,
            count  : recordCount,
            records: records,
            success: success,
            message: message
        });
    },

    
    extractData : function(root) {
        var me = this,
            Model   = me.model,
            length  = root.length,
            records = new Array(length),
            convertedValues, node, record, i;

        if (!root.length && Ext.isObject(root)) {
            root = [root];
            length = 1;
        }

        for (i = 0; i < length; i++) {
            node = root[i];
            if (node.isModel) {
                
                
                records[i] = node;
            } else {
                
                
                
                records[i] = record = new Model(undefined, me.getId(node), node, convertedValues = {});

                
                
                record.phantom = false;

                
                me.convertRecordData(convertedValues, node, record);

                if (me.implicitIncludes && record.associations.length) {
                    me.readAssociated(record, node);
                }
            }
        }

        return records;
    },

    
    readAssociated: function(record, data) {
        var associations = record.associations.items,
            i            = 0,
            length       = associations.length,
            association, associationData, proxy, reader;
        
        for (; i < length; i++) {
            association     = associations[i];
            associationData = this.getAssociatedDataRoot(data, association.associationKeyFunction || association.associationKey || association.name);
            
            if (associationData) {
                reader = association.getReader();
                if (!reader) {
                    proxy = association.associatedModel.getProxy();
                    
                    if (proxy) {
                        reader = proxy.getReader();
                    } else {
                        reader = new this.constructor({
                            model: association.associatedName
                        });
                    }
                }
                association.read(record, reader, associationData);
            }  
        }
    },
    
    
    getAssociatedDataRoot: function(data, associationName) {
        if (Ext.isFunction(associationName)) {
            return associationName(data);
        }

        return data[associationName];
    },
    
    getFields: function() {
        return this.model.prototype.fields.items;
    },

    
    getData: Ext.identityFn,

    
    getRoot: Ext.identityFn,

    
    getResponseData: function(response) {
    },

    
    onMetaChange : function(meta) {
        var me = this,
            fields = meta.fields || me.getFields(),
            newModel,
            clientIdProperty;
        
        
        me.metaData = meta;
        
        
        me.root = meta.root || me.root;
        me.idProperty = meta.idProperty || me.idProperty;
        me.totalProperty = meta.totalProperty || me.totalProperty;
        me.successProperty = meta.successProperty || me.successProperty;
        me.messageProperty = meta.messageProperty || me.messageProperty;
        clientIdProperty = meta.clientIdProperty;

        if (me.model) {
            me.model.setFields(fields, me.idProperty, clientIdProperty);
            me.setModel(me.model, true);
        }
        else {
            newModel = Ext.define("Ext.data.reader.Json-Model" + Ext.id(), {
                extend: 'Ext.data.Model',
                fields: fields,
                clientIdProperty: clientIdProperty
            });
            if (me.idProperty) {
                
                
                
                
                newModel.idProperty = me.idProperty;
            }
            me.setModel(newModel, true);
        }
    },
    
    
    getIdProperty: function() {
        var idField = this.model.prototype.idField,
            idProperty = this.idProperty;

        if (!idProperty && idField  && (idProperty = idField.mapping) == null) {
            idProperty = idField.name;
        }
        return idProperty;
    },

    
    buildExtractors: function(force) {
        var me          = this,
            idProp      = me.getIdProperty(),
            totalProp   = me.totalProperty,
            successProp = me.successProperty,
            messageProp = me.messageProperty,
            accessor;
            
        if (force === true) {
            delete me.convertRecordData;
        }
        
        if (me.convertRecordData) {
            return;
        }   

        
        if (totalProp) {
            me.getTotal = me.createAccessor(totalProp);
        }

        if (successProp) {
            me.getSuccess = me.createAccessor(successProp);
        }

        if (messageProp) {
            me.getMessage = me.createAccessor(messageProp);
        }

        
        if (idProp) {
            accessor = me.createAccessor(idProp);
            me.getId = function(record) {
                var id = accessor.call(me, record);
                return (id === undefined || id === '') ? null : id;
            };
        } else {
            me.getId = function() {
                return null;
            };
        }
        me.convertRecordData = me.buildRecordDataExtractor();
        me.lastFieldGeneration = me.model.prototype.fields.generation;
    },

    recordDataExtractorTemplate : [
        'var me = this\n',
        '    ,fields = me.model.prototype.fields\n',
        '    ,value\n',
        '    ,internalId\n',
        '<tpl for="fields">',
        '    ,__field{#} = fields.map["{name}"]\n',
        '</tpl>', ';\n',

        'return function(dest, source, record) {\n',
        '<tpl for="fields">',
        '{% var fieldAccessExpression =  this.createFieldAccessExpression(values, "__field" + xindex, "source");',
        '   if (fieldAccessExpression) { %}',
        
        '    value = {[ this.createFieldAccessExpression(values, "__field" + xindex, "source") ]};\n',

        
            '<tpl if="hasCustomConvert">',
        '    dest["{name}"] = value === undefined ? __field{#}.convert(__field{#}.defaultValue, record) : __field{#}.convert(value, record);\n',

        
            '<tpl elseif="defaultValue !== undefined">',
        '    if (value === undefined) {\n',
        '        if (me.applyDefaults) {\n',
                '<tpl if="convert">',
        '            dest["{name}"] = __field{#}.convert(__field{#}.defaultValue, record);\n',
                '<tpl else>',
        '            dest["{name}"] = __field{#}.defaultValue\n',
                '</tpl>',
        '        };\n',
        '    } else {\n',
                '<tpl if="convert">',
        '        dest["{name}"] = __field{#}.convert(value, record);\n',
                '<tpl else>',
        '        dest["{name}"] = value;\n',
                '</tpl>',
        '    };\n',

        
            '<tpl else>',
        '    if (value !== undefined) {\n',
                '<tpl if="convert">',
        '        dest["{name}"] = __field{#}.convert(value, record);\n',
                '<tpl else>',
        '        dest["{name}"] = value;\n',
                '</tpl>',
        '    }\n',
            '</tpl>',
            
        
        
        
        '{% } else { %}',
            '<tpl if="defaultValue !== undefined">',
                '<tpl if="convert">',
        '    dest["{name}"] = __field{#}.convert(__field{#}.defaultValue, record);\n',
                '<tpl else>',
        '    dest["{name}"] = __field{#}.defaultValue\n',
                '</tpl>',
            '</tpl>',
        '{% } %}',
        '</tpl>',

        
        
        
        '<tpl if="clientIdProp">',
        '    if (record && (internalId = {[ this.createFieldAccessExpression(\{mapping: values.clientIdProp\}, null, "source") ]})) {\n',
        '        record.{["internalId"]} = internalId;\n',
        '    }\n',
        '</tpl>',

        '};'
    ],

    
    buildRecordDataExtractor: function() {
        var me = this,
            modelProto = me.model.prototype,
            templateData = {
                clientIdProp: modelProto.clientIdProperty,
                fields: modelProto.fields.items
            };

        me.recordDataExtractorTemplate.createFieldAccessExpression = function() { 
            return me.createFieldAccessExpression.apply(me,arguments);
        };
        
        
        
        
        
        return Ext.functionFactory(me.recordDataExtractorTemplate.apply(templateData)).call(me);
    },

    destroyReader: function() {
        var me = this;
        delete me.proxy;
        delete me.model;
        delete me.convertRecordData;
        delete me.getId;
        delete me.getTotal;
        delete me.getSuccess;
        delete me.getMessage;
    }
}, function() {
    var proto = this.prototype;
    Ext.apply(proto, {
        
        nullResultSet: new Ext.data.ResultSet({
            total  : 0,
            count  : 0,
            records: [],
            success: true,
            message: ''
        }),
        recordDataExtractorTemplate: new Ext.XTemplate(proto.recordDataExtractorTemplate)
    });
});


Ext.define('Ext.data.reader.Json', {
    extend:  Ext.data.reader.Reader ,
    alternateClassName: 'Ext.data.JsonReader',
    alias : 'reader.json',

    root: '',

    
    
    
    metaProperty: 'metaData',

    
    useSimpleAccessors: false,

    
    readRecords: function(data) {
        var me = this,
            meta;
            
        
        if (me.getMeta) {
            meta = me.getMeta(data);
            if (meta) {
                me.onMetaChange(meta);
            }
        } else if (data.metaData) {
            me.onMetaChange(data.metaData);
        }

        
        me.jsonData = data;
        return me.callParent([data]);
    },

    
    getResponseData: function(response) {
        var data, error;
 
        try {
            data = Ext.decode(response.responseText);
            return this.readRecords(data);
        } catch (ex) {
            error = new Ext.data.ResultSet({
                total  : 0,
                count  : 0,
                records: [],
                success: false,
                message: ex.message
            });

            this.fireEvent('exception', this, response, error);

            Ext.Logger.warn('Unable to parse the JSON returned by the server');

            return error;
        }
    },

    
    buildExtractors : function() {
        var me = this,
            metaProp = me.metaProperty;

        me.callParent(arguments);

        if (me.root) {
            me.getRoot = me.createAccessor(me.root);
        } else {
            me.getRoot = Ext.identityFn;
        }
        
        if (metaProp) {
            me.getMeta = me.createAccessor(metaProp);
        }
    },

    
    extractData: function(root) {
        var recordName = this.record,
            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]);
    },

    
    createAccessor: (function() {
        var re = /[\[\.]/;

        return function(expr) {
            if (Ext.isEmpty(expr)) {
                return Ext.emptyFn;
            }
            if (Ext.isFunction(expr)) {
                return expr;
            }
            if (this.useSimpleAccessors !== true) {
                var i = String(expr).search(re);
                if (i >= 0) {
                    return Ext.functionFactory('obj', 'return obj' + (i > 0 ? '.' : '') + expr);
                }
            }
            return function(obj) {
                return obj[expr];
            };
        };
    }()),

    
    createFieldAccessExpression: (function() {
        var re = /[\[\.]/;

        return function(field, fieldVarName, dataName) {
            var mapping = field.mapping,
                hasMap = mapping || mapping === 0,
                map    = hasMap ? mapping : field.name,
                result,
                operatorIndex;

            
            if (mapping === false) {
                return;
            }

            if (typeof map === 'function') {
                result = fieldVarName + '.mapping(' + dataName + ', this)';
            } else if (this.useSimpleAccessors === true || ((operatorIndex = String(map).search(re)) < 0)) {
                if (!hasMap || isNaN(map)) {
                    
                    map = '"' + map + '"';
                }
                result = dataName + "[" + map + "]";
            } else if (operatorIndex === 0) {
                
                
                result = dataName + map;
            } else {
                
                
                
                
                
                
                var parts = map.split('.'),
                    len = parts.length,
                    i = 1,
                    tempResult = dataName + '.' + parts[0],
                    buffer = [tempResult]; 
                
                for (; i < len; i++) {
                    tempResult += '.' + parts[i];
                    buffer.push(tempResult);
                }
                result = buffer.join(' && ');
            }
            return result;
        };
    }())
});


Ext.define('Ext.data.writer.Writer', {
    alias: 'writer.base',
    alternateClassName: ['Ext.data.DataWriter', 'Ext.data.Writer'],

    
    writeAllFields: true,

    

    
    nameProperty: 'name',

    
    writeRecordId: true,

    
    isWriter: true,

    
    constructor: function(config) {
        Ext.apply(this, config);
    },

    
    write: function(request) {
        var operation = request.operation,
            records   = operation.records || [],
            len       = records.length,
            i         = 0,
            data      = [];

        for (; i < len; i++) {
            data.push(this.getRecordData(records[i], operation));
        }
        return this.writeRecords(request, data);
    },

    
    getRecordData: function(record, operation) {
        var isPhantom = record.phantom === true,
            writeAll = this.writeAllFields || isPhantom,
            fields = record.fields,
            fieldItems = fields.items,
            data = {},
            clientIdProperty = record.clientIdProperty,
            changes,
            field,
            key,
            mappedIdProperty,
            f, fLen;

        if (writeAll) {
            fLen = fieldItems.length;

            for (f = 0; f < fLen; f++) {
                field = fieldItems[f];
                if (field.persist) {
                    this.writeValue(data, field, record);
                }
            }
        } else if (operation.action === 'destroy') {
            this.writeValue(data, record.idField, record);
        } else {
            
            changes = record.getChanges();
            for (key in changes) {
                if (changes.hasOwnProperty(key)) {
                    field = fields.get(key);
                    if (field.persist) {
                        this.writeValue(data, field, record);
                    }
                }
            }
        }
        if (isPhantom) {
            if (clientIdProperty && operation && operation.records.length > 1) {
                
                
                data[clientIdProperty] = record.internalId;
            }
        } else if (this.writeRecordId) {
            
            mappedIdProperty = fields.get(record.idProperty)[this.nameProperty] || record.idProperty;
            data[mappedIdProperty] = record.getId();
        }

        return data;
    },

    writeValue: function(data, field, record){
        var name = field[this.nameProperty],
            dateFormat = this.dateFormat || field.dateWriteFormat || field.dateFormat,
            value = record.get(field.name);

        
        
        if (name == null) {
            name = field.name;
        }

        if (field.serialize) {
            data[name] = field.serialize(value, record);
        } else if (field.type === Ext.data.Types.DATE && dateFormat && Ext.isDate(value)) {
            data[name] = Ext.Date.format(value, dateFormat);
        } else {
            data[name] = value;
        }
    }
});


Ext.define('Ext.data.writer.Json', {
    extend:  Ext.data.writer.Writer ,
    alternateClassName: 'Ext.data.JsonWriter',
    alias: 'writer.json',
    
    
    root: undefined,
    
    
    encode: false,
    
    
    allowSingle: true,
    
    
    expandData: false,
    
    
    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 root = this.root;
        
        if (this.expandData) {
            data = this.getExpandedData(data);
        }
        
        if (this.allowSingle && data.length === 1) {
            
            data = data[0];
        }
        
        if (this.encode) {
            if (root) {
                
                request.params[root] = Ext.encode(data);
            } else {
            }
        } else {
            
            request.jsonData = request.jsonData || {};
            if (root) {
                request.jsonData[root] = data;
            } else {
                request.jsonData = data;
            }
        }
        return request;
    }
});


Ext.define('Ext.data.proxy.Proxy', {
    alias: 'proxy.proxy',
    alternateClassName: ['Ext.data.DataProxy', 'Ext.data.Proxy'],

               
                               
                              
      

           
                         
                             
                        
      

    mixins: {
        observable:  Ext.util.Observable 
    },

    
    batchOrder: 'create,update,destroy',

    
    batchActions: true,

    
    defaultReaderType: 'json',

    
    defaultWriterType: 'json',

    

    

    

    
    isProxy: true,

    
     isSynchronous: false,

    
    constructor: function(config) {
        var me = this;

        config = config || {};
        me.proxyConfig = config;

        me.mixins.observable.constructor.call(me, config);

        if (me.model !== undefined && !(me.model instanceof Ext.data.Model)) {
            me.setModel(me.model);
        } else {
            if (me.reader) {
                me.setReader(me.reader);
            }
            
            if (me.writer) {
                me.setWriter(me.writer);
            }
        }

        
    },

    
    setModel: function(model, setOnStore) {
        var me = this;
        
        me.model = Ext.ModelManager.getModel(model);

        me.setReader(this.reader);
        me.setWriter(this.writer);

        if (setOnStore && me.store) {
            me.store.setModel(me.model);
        }
    },

    
    getModel: function() {
        return this.model;
    },

    
    setReader: function(reader) {
        var me = this,
            needsCopy = true,
            current = me.reader;

        if (reader === undefined || typeof reader == 'string') {
            reader = {
                type: reader
            };
            needsCopy = false;
        }

        if (reader.isReader) {
            reader.setModel(me.model);
        } else {
            if (needsCopy) {
                reader = Ext.apply({}, reader);
            }
            Ext.applyIf(reader, {
                proxy: me,
                model: me.model,
                type : me.defaultReaderType
            });

            reader = Ext.createByAlias('reader.' + reader.type, reader);
        }

        if (reader !== current && reader.onMetaChange) {
            reader.onMetaChange = Ext.Function.createSequence(reader.onMetaChange, this.onMetaChange, this);
        }

        me.reader = reader;
        return me.reader;
    },

    
    getReader: function() {
        return this.reader;
    },

    
    onMetaChange: function(meta) {
        this.fireEvent('metachange', this, meta);
    },

    
    setWriter: function(writer) {
        var me = this,
            needsCopy = true;
            
        if (writer === undefined || typeof writer == 'string') {
            writer = {
                type: writer
            };
            needsCopy = false;
        }

        if (!writer.isWriter) {
            if (needsCopy) {
                writer = Ext.apply({}, writer);
            }
            Ext.applyIf(writer, {
                model: me.model,
                type : me.defaultWriterType
            });

            writer = Ext.createByAlias('writer.' + writer.type, writer);
        }

        me.writer = writer;

        return me.writer;
    },

    
    getWriter: function() {
        return this.writer;
    },

    
    create: Ext.emptyFn,

    
    read: Ext.emptyFn,

    
    update: Ext.emptyFn,

    
    destroy: Ext.emptyFn,

    
    batch: function(options, listeners) {
        var me = this,
            useBatch = me.batchActions,
            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.batchOrder.split(',');
        aLen    = actions.length;

        for (a = 0; a < aLen; a++) {
            action  = actions[a];
            records = options.operations[action];

            if (records) {
                if (useBatch) {
                    batch.add(new Ext.data.Operation({
                        action  : action,
                        records : records
                    }));
                } else {
                    rLen = records.length;

                    for (r = 0; r < rLen; r++) {
                        record = records[r];

                        batch.add(new Ext.data.Operation({
                            action  : action,
                            records : [record]
                        }));
                    }
                }
            }
        }

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

    clone: function() {
        return new this.self(this.proxyConfig);
    }
});


Ext.define('Ext.data.Operation', {
    
    synchronous: true,

    
    action: undefined,

    
    filters: undefined,

    
    sorters: undefined,

    
    groupers: undefined,

    
    start: undefined,

    
    limit: undefined,

    
    batch: undefined,
    
    

    
    callback: undefined,

    
    scope: undefined,

    
    started: false,

    
    running: false,

    
    complete: false,

    
    success: undefined,

    
    exception: false,

    
    error: undefined,

    
    actionCommitRecordsRe: /^(?:create|update)$/i,

    
    actionSkipSyncRe: /^destroy$/i,

    
    constructor: function(config) {
        Ext.apply(this, config || {});
    },

    
    commitRecords: function(serverRecords) {
        var me = this,
            commitRecords = me.actionCommitRecordsRe.test(me.action),
            mc, index, clientRecords, serverRec, clientRec, i, len,
            modifiedFields, recordModifiedFields;

        if (!me.actionSkipSyncRe.test(me.action)) {
            clientRecords = me.records;

            if (clientRecords && clientRecords.length) {

                
                
                
                
                if (commitRecords) {
                    recordModifiedFields = [];
                }
                if (clientRecords.length > 1) {
                    
                    
                    
                    
                    if (me.action == 'update' || clientRecords[0].clientIdProperty) {
                        mc = new Ext.util.MixedCollection();
                        mc.addAll(serverRecords);

                        for (index = clientRecords.length; index--; ) {
                            clientRec = clientRecords[index];
                            serverRec = mc.findBy(me.matchClientRec, clientRec);

                            
                            modifiedFields = clientRec.copyFrom(serverRec);

                            
                            if (commitRecords) {
                                recordModifiedFields.push(modifiedFields);
                            }
                        }
                    } else {
                        for (i = 0, len = clientRecords.length; i < len; ++i) {
                            clientRec = clientRecords[i];
                            serverRec = serverRecords[i];
                            if (clientRec && serverRec) {
                                modifiedFields = me.updateRecord(clientRec, serverRec);

                                
                                if (commitRecords) {
                                    recordModifiedFields.push(modifiedFields);
                                }
                            }
                        }
                    }
                } else {
                    
                    modifiedFields = me.updateRecord(clientRecords[0], serverRecords[0]);   

                    
                    if (commitRecords) {
                        recordModifiedFields[0] = modifiedFields;
                    }
                }

                if (commitRecords) {
                    for (index = clientRecords.length; index--; ) {

                        
                        
                        
                        clientRecords[index].commit(false, recordModifiedFields[index]);
                    }
                }
            }
        }
    },
    
    updateRecord: function(clientRec, serverRec) {
        
        if (serverRec && (clientRec.phantom || clientRec.getId() === serverRec.getId())) {
            return clientRec.copyFrom(serverRec);
        }

        
        return [];
    },

    
    
    
    matchClientRec: function(record) {
        var clientRec = this,
            clientRecordId = clientRec.getId();

        if(clientRecordId && record.getId() === clientRecordId) {
            return true;
        }
        
        
        
        return record.internalId === clientRec.internalId;
    },

    
    setStarted: function() {
        this.started = true;
        this.running = true;
    },

    
    setCompleted: function() {
        this.complete = true;
        this.running  = false;
    },

    
    setSuccessful: function() {
        this.success = true;
    },

    
    setException: function(error) {
        this.exception = true;
        this.success = false;
        this.running = false;
        this.error = error;
    },

    
    hasException: function() {
        return this.exception === true;
    },

    
    getError: function() {
        return this.error;
    },

    
    getRecords: function() {
        var resultSet = this.getResultSet();
        return this.records || (resultSet ? resultSet.records : null);
    },

    
    getResultSet: function() {
        return this.resultSet;
    },

    
    isStarted: function() {
        return this.started === true;
    },

    
    isRunning: function() {
        return this.running === true;
    },

    
    isComplete: function() {
        return this.complete === true;
    },

    
    wasSuccessful: function() {
        return this.isComplete() && this.success === true;
    },

    
    setBatch: function(batch) {
        this.batch = batch;
    },

    
    allowWrite: function() {
        return this.action != 'read';
    }
});


Ext.define('Ext.data.AbstractStore', {
               
                                   
                               
                             
                         
      

    mixins: {
        observable:  Ext.util.Observable ,
        sortable:  Ext.util.Sortable 
    },

    statics: {
        
        create: function(store) {
            if (!store.isStore) {
                if (!store.type) {
                    store.type = 'store';
                }
                store = Ext.createByAlias('store.' + store.type, store);
            }
            return store;
        }
    },

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

    
    remoteSort  : false,

    
    remoteFilter: false,

    

    
    autoLoad: undefined,

    
    autoSync: false,

    
    batchUpdateMode: 'operation',

    
    filterOnLoad: true,

    
    sortOnLoad: true,

    
    implicitModel: false,

    
    defaultProxyType: 'memory',

    
    isDestroyed: false,

    
    isStore: true,

    

    

    

    

    

    sortRoot: 'data',

    
    constructor: function(config) {
        var me = this,
            filters;

        

        

        

        

        
        
        

        

        

        

        
        
        

        Ext.apply(me, config);
        

        
        me.removed = [];

        me.mixins.observable.constructor.apply(me, arguments);


        me.model = Ext.ModelManager.getModel(me.model);

        
        Ext.applyIf(me, {
            modelDefaults: null
        });

        
        if (!me.model && me.fields) {
            me.model = Ext.define('Ext.data.Store.ImplicitModel-' + (me.storeId || Ext.id()), {
                extend: 'Ext.data.Model',
                fields: me.fields,
                proxy: me.proxy || me.defaultProxyType
            });

            delete me.fields;

            me.implicitModel = true;
        }


        
        me.setProxy(me.proxy || me.model.getProxy());

        if (me.id && !me.storeId) {
            me.storeId = me.id;
            delete me.id;
        }

        if (me.storeId) {
            Ext.data.StoreManager.register(me);
        }

        me.mixins.sortable.initSortable.call(me);

        
        filters = me.decodeFilters(me.filters);
        me.filters = new Ext.util.MixedCollection();
        me.filters.addAll(filters);
    },

    
    setProxy: function(proxy) {
        var me = this,
            model = me.model;

        if (proxy instanceof Ext.data.proxy.Proxy) {
            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);
        }

        if (!me.disableMetaChangeEvent) {
            proxy.on('metachange', me.onMetaChange, me);
        }

        me.proxy = proxy;

        return me.proxy;
    },

    
    getProxy: function() {
        return this.proxy;
    },

    
    onMetaChange: function(proxy, meta) {
        this.fireEvent('metachange', this, meta);
    },

    
    create: function(data, options) {
        var me = this,
            instance = Ext.ModelManager.create(Ext.applyIf(data, me.modelDefaults), me.model.modelName),
            operation;

        options = options || {};

        Ext.applyIf(options, {
            action : 'create',
            records: [instance]
        });

        operation = new Ext.data.Operation(options);

        me.proxy.create(operation, me.onProxyWrite, me);

        return instance;
    },

    read: function() {
        return this.load.apply(this, arguments);
    },

    update: function(options) {
        var me = this,
            operation;
        options = options || {};

        Ext.applyIf(options, {
            action : 'update',
            records: me.getUpdatedRecords()
        });

        operation = new Ext.data.Operation(options);

        return me.proxy.update(operation, me.onProxyWrite, me);
    },

    
    onProxyWrite: function(operation) {
        var me = this,
            success = operation.wasSuccessful(),
            records = operation.getRecords();

        switch (operation.action) {
            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);
            me.fireEvent('refresh', me);
        }
        
        Ext.callback(operation.callback, operation.scope || me, [records, operation, success]);
    },
    
    
    onCreateRecords: Ext.emptyFn,
    
    
    onUpdateRecords: Ext.emptyFn,
    
    
    onDestroyRecords: function(records, operation, success) {
        if (success) {
            this.removed = [];
        }
    },

    
    
    destroy: function(options) {
        var me = this,
            operation;

        options = options || {};

        Ext.applyIf(options, {
            action : 'destroy',
            records: me.getRemovedRecords()
        });

        operation = new Ext.data.Operation(options);

        return me.proxy.destroy(operation, me.onProxyWrite, me);
    },

    
    onBatchOperationComplete: function(batch, operation) {
        return this.onProxyWrite(operation);
    },

    
    onBatchComplete: function(batch, operation) {
        var me = this,
            operations = batch.operations,
            length = operations.length,
            i;

        me.suspendEvents();

        for (i = 0; i < length; i++) {
            me.onProxyWrite(operations[i]);
        }

        me.resumeEvents();

        me.fireEvent('datachanged', me);
        me.fireEvent('refresh', me);
    },

    
    onBatchException: function(batch, operation) {
        
        
        
        
        
    },

    
    filterNew: function(item) {
        
        return item.phantom === true && item.isValid();
    },

    
    getNewRecords: function() {
        return [];
    },

    
    getUpdatedRecords: function() {
        return [];
    },

    
    getModifiedRecords : function(){
        return [].concat(this.getNewRecords(), this.getUpdatedRecords());
    },
    
    
    filterUpdated: function(item) {
        
        return item.dirty === true && item.phantom !== true && item.isValid();
    },

    
    getRemovedRecords: function() {
        return this.removed;
    },

    filter: function(filters, value) {

    },

    
    decodeFilters: function(filters) {
        if (!Ext.isArray(filters)) {
            if (filters === undefined) {
                filters = [];
            } else {
                filters = [filters];
            }
        }

        var length = filters.length,
            Filter = Ext.util.Filter,
            config, i;

        for (i = 0; i < length; i++) {
            config = filters[i];

            if (!(config instanceof Filter)) {
                Ext.apply(config, {
                    root: 'data'
                });

                
                if (config.fn) {
                    config.filterFn = config.fn;
                }

                
                if (typeof config == 'function') {
                    config = {
                        filterFn: config
                    };
                }

                filters[i] = new Filter(config);
            }
        }

        return filters;
    },

    clearFilter: function(supressEvent) {

    },

    isFiltered: function() {

    },

    filterBy: function(fn, scope) {

    },

    
    sync: function(options) {
        var me = this,
            operations = {},
            toCreate = me.getNewRecords(),
            toUpdate = me.getUpdatedRecords(),
            toDestroy = me.getRemovedRecords(),
            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) {
            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
            };

        if (me.batchUpdateMode == 'operation') {
            listeners.operationcomplete = me.onBatchOperationComplete;
        } else {
            listeners.complete = me.onBatchComplete;
        }

        return listeners;
    },

    
    save: function() {
        return this.sync.apply(this, arguments);
    },

    
    load: function(options) {
        var me = this,
            operation = {
                action: 'read'
            };

        
        if (me.remoteFilter) {
            operation.filters = me.filters.items;
        }
        if (me.remoteSort) {
            operation.sorters = me.getSorters();
        }
        Ext.apply(operation, options);
        me.lastOptions = operation;

        operation = new Ext.data.Operation(operation);

        if (me.fireEvent('beforeload', me, operation) !== false) {
            me.loading = true;
            me.proxy.read(operation, me.onProxyLoad, me);
        }

        return me;
    },

    
    reload: function(options) {
        var o = Ext.apply({}, options, this.lastOptions);
        return this.load(o);
    },

    
    afterEdit : function(record, modifiedFieldNames) {
        var me = this,
            i, shouldSync;

        if (me.autoSync && !me.autoSyncSuspended) {
            for (i = modifiedFieldNames.length; i--;) {
                
                if (record.fields.get(modifiedFieldNames[i]).persist) {
                    shouldSync = true;
                    break;
                }
            }
            if (shouldSync) {
                me.sync();
            }
        }
        me.onUpdate(record, Ext.data.Model.EDIT, modifiedFieldNames);
        me.fireEvent('update', me, record, Ext.data.Model.EDIT, modifiedFieldNames);
    },

    
    afterReject : function(record) {
        
        
        
        
        
        this.onUpdate(record, Ext.data.Model.REJECT, null);
        this.fireEvent('update', this, record, Ext.data.Model.REJECT, null);
    },

    
    afterCommit : function(record, modifiedFieldNames) {
        if (!modifiedFieldNames) {
            modifiedFieldNames = null;
        }
        this.onUpdate(record, Ext.data.Model.COMMIT, modifiedFieldNames);
        this.fireEvent('update', this, record, Ext.data.Model.COMMIT, modifiedFieldNames);
    },

    onUpdate: Ext.emptyFn,

    onIdChanged: function(model, oldId, newId, oldInternalId){
        this.fireEvent('idchanged', this, model, oldId, newId, oldInternalId);
    },

    
    destroyStore: function() {
        var implicitModelName,
            me = this;

        if (!me.isDestroyed) {
            me.clearListeners();
            if (me.storeId) {
                Ext.data.StoreManager.unregister(me);
            }
            me.clearData();
            me.data = me.tree = me.sorters = me.filters = me.groupers = null;
            if (me.reader) {
                me.reader.destroyReader();
            }
            me.proxy = me.reader = me.writer = null;
            me.isDestroyed = true;

            if (me.implicitModel) {
                implicitModelName = Ext.getClassName(me.model);
                Ext.undefine(implicitModelName);
                Ext.ModelManager.unregisterType(implicitModelName);
            } else {
                me.model = null;
            }
        }
    },
    
    
    getState: function() {
        var me = this,
            hasState,
            result,
            hasGroupers = !!me.groupers,
            groupers = [],
            sorters = [],
            filters = [];

        if (hasGroupers) {
            me.groupers.each(function(g) {
                groupers[groupers.length] = g.serialize();
                hasState = true;
            });
        }

        if (me.sorters) {
            
            me.sorters.each(function(s) {
                
                if (hasGroupers && !me.groupers.contains(s)) {
                    sorters[sorters.length] = s.serialize();
                    hasState = true;
                }
            });
        }

        
        
        if (me.filters && me.statefulFilters) {
            me.filters.each(function(f) {
                filters[filters.length] = f.serialize();
                hasState = true;
            });
        }

        
        if (hasState) {
            result = {};
            if (groupers.length) {
                result.groupers = groupers;
            }
            if (sorters.length) {
                result.sorters = sorters;
            }
            if (filters.length) {
                result.filters = filters;
            }
            return result;
        }
    },

    
    applyState: function(state) {
        var me = this,
            hasSorters = !!me.sorters,
            hasGroupers = !!me.groupers,
            hasFilters = !!me.filters,
            locallySorted;

        if (hasGroupers && state.groupers) {
            me.groupers.clear();
            me.groupers.addAll(me.decodeGroupers(state.groupers));
        }

        if (hasSorters && state.sorters) {
            me.sorters.clear();
            me.sorters.addAll(me.decodeSorters(state.sorters));
        }

        if (hasFilters && state.filters) {
            me.filters.clear();
            me.filters.addAll(me.decodeFilters(state.filters));
        }

        if (hasSorters && hasGroupers) {
            
            me.sorters.insert(0, me.groupers.getRange());
        }

        
        if (me.autoLoad && (me.remoteSort || me.remoteGroup || me.remoteFilter)) {
            if (me.autoLoad === true) {
                me.reload();
            } else {
                me.reload(me.autoLoad);
            }
        }

        
        if (hasFilters && me.filters.length && !me.remoteFilter) {
            me.filter();
            locallySorted = me.sortOnFilter;
        }

        
        if (hasSorters && me.sorters.length && !me.remoteSort && !locallySorted) {
            me.sort();
        }
    },

    
    doSort: function(sorterFn) {
        var me = this;
        if (me.remoteSort) {
            
            me.load();
        } else {
            me.data.sortBy(sorterFn);
            me.fireEvent('datachanged', me);
            me.fireEvent('refresh', me);
        }
        me.fireEvent('sort', me, me.sorters.getRange());
    },

    
    clearData: Ext.emptyFn,
    
    
    getCount: Ext.emptyFn,

    
    getById: Ext.emptyFn,

    
    removeAll: Ext.emptyFn,
    
    

    
    isLoading: function() {
        return !!this.loading;
    },

    
    suspendAutoSync: function() {
        this.autoSyncSuspended = true;
    },

    
    resumeAutoSync: function() {
        this.autoSyncSuspended = false;
    }

});



Ext.define('Ext.app.domain.Store', {
    extend:  Ext.app.EventDomain ,
    singleton: true,
    
               
                                
      
    
    type: 'store',
    idProperty: 'storeId',
    
    constructor: function() {
        var me = this;
        
        me.callParent();
        me.monitor(Ext.data.AbstractStore);
    }
});


Ext.define('Ext.app.Controller', {
               
                           
                           
                                
                               
                                
                                   
                              
      
    
           
                                   
      

    mixins: {
        observable:  Ext.util.Observable 
    },

    

    statics: {
        strings: {
            model: {
                getter: 'getModel',
                upper: 'Model'
            },

            view: {
                getter: 'getView',
                upper: 'View'
            },

            controller: {
                getter: 'getController',
                upper: 'Controller'
            },

            store: {
                getter: 'getStore',
                upper: 'Store'
            }
        },

        controllerRegex: /^(.*)\.controller\./,

        createGetter: function(baseGetter, name) {
            return function () {
                return this[baseGetter](name);
            };
        },

        getGetterName: function(name, kindUpper) {
            var fn       = 'get',
                parts    = name.split('.'),
                numParts = parts.length,
                index;

            
            for (index = 0; index < numParts; index++) {
                fn += Ext.String.capitalize(parts[index]);
            }

            fn += kindUpper;
            
            return fn;
        },

        
        processDependencies: function(cls, requires, namespace, kind, names) {
            if (!names || !names.length) {
                return;
            }

            var me = this,
                strings = me.strings[kind],
                o, absoluteName, shortName, name, j, subLn, getterName, getter;
                
             if (!Ext.isArray(names)) {
                 names = [names];
             }

            for (j = 0, subLn = names.length; j < subLn; j++) {
                name = names[j];
                o = me.getFullName(name, kind, namespace);
                absoluteName = o.absoluteName;
                shortName = o.shortName;

                requires.push(absoluteName);
                getterName = me.getGetterName(shortName, strings.upper);
                cls[getterName] = getter = me.createGetter(strings.getter, name);

                
                if (kind !== 'controller') {
                    
                    
                    
                    
                    getter['Ext.app.getter'] = true;
                }
            }
        },

        getFullName: function(name, kind, namespace) {
            var shortName = name,
                sep, absoluteName;

            if ((sep = name.indexOf('@')) > 0) {
                
                
                
                
                shortName    = name.substring(0, sep); 
                absoluteName = name.substring(sep + 1) + '.' + shortName; 
            }
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            else if (name.indexOf('.') > 0 && (Ext.ClassManager.isCreated(name) ||
                     Ext.Loader.isAClassNameWithAKnownPrefix(name))) {
                absoluteName = name;
            }
            else {

                if (namespace) {
                    absoluteName = namespace + '.' + kind + '.' + name;
                    shortName    = name;
                }
                else {
                    absoluteName = name;
                }
            }

            return {
                absoluteName: absoluteName,
                shortName:    shortName
            };
        }
    },
    
    
    application: null,

    

    

    

    

    onClassExtended: function(cls, data, hooks) {
        var onBeforeClassCreated = hooks.onBeforeCreated;

        hooks.onBeforeCreated = function(cls, data) {
            var Controller = Ext.app.Controller,
                ctrlRegex  = Controller.controllerRegex,
                requires   = [],
                className, namespace, requires, proto, match;

            proto = cls.prototype;
            
            
            className = Ext.getClassName(cls);
            namespace = data.$namespace                 ||
                        Ext.app.getNamespace(className) ||
                        ((match = ctrlRegex.exec(className)) && match[1]);

            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) {
        var me = this;

        me.mixins.observable.constructor.call(me, config);

        if (me.refs) {
            me.ref(me.refs);
        }

        me.eventbus = Ext.app.EventBus;
        
        me.initAutoGetters();
    },
    
    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,

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

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

    
    getController: function(id) {
        var me = this,
            app = me.application;

        if (id === me.id) {
            return me;
        }

        return app && app.getController(id);
    },

    
    getStore: function(name) {
        var storeId, store;

        storeId = (name.indexOf('@') == -1) ? name : name.split('@')[0];
        store   = Ext.StoreManager.get(storeId);

        if (!store) {
            name = Ext.app.Controller.getFullName(name, 'store', this.$namespace);

            if (name) {
                store = Ext.create(name.absoluteName, {
                    storeId: storeId
                });
            }
        }

        return store;
    },

    
    getModel: function(model) {
        var name = Ext.app.Controller.getFullName(model, 'model', this.$namespace);

        return name && Ext.ModelManager.getModel(name.absoluteName);
    },

    
    getView: function(view) {
        var name = Ext.app.Controller.getFullName(view, 'view', this.$namespace);

        return name && Ext.ClassManager.get(name.absoluteName);
    },

    
    getApplication: function() {
        return this.application;
    }
});


Ext.define('Ext.container.DockingContainer', {

    

                                                           

    

    isDockingContainer: true,
    
    
    
    

    
    defaultDockWeights: {
        top:    { render: 1, visual: 1 },
        left:   { render: 3, visual: 5 },
        right:  { render: 5, visual: 7 },
        bottom: { render: 7, visual: 3 }
    },

    
    
    
    
    dockOrder: {
        top: -1,
        left: -1,
        right: 1,
        bottom: 1
    },

    
    horizontalDocks: 0,

    
    addDocked : function(items, pos) {
        var me = this,
            i = 0,
            item, length;

        items = me.prepareItems(items);
        length = items.length;

        for (; i < length; i++) {
            item = items[i];
            item.dock = item.dock || 'top';
            if (item.dock === 'left' || item.dock === 'right') {
                me.horizontalDocks++;
            }

            if (pos !== undefined) {
                i += pos;
                me.dockedItems.insert(i, item);
            } else {
                me.dockedItems.add(item);
            }
            
            item.onAdded(me, i);
            if (me.hasListeners.dockedadd) {
                me.fireEvent('dockedadd', me, item, i);
            }
            if (me.onDockedAdd !== Ext.emptyFn) {
                me.onDockedAdd(item);
            }
        }

        if (me.rendered && !me.suspendLayout) {
            me.updateLayout();
        }
        return items;
    },

    destroyDockedItems: function(){
        var dockedItems = this.dockedItems,
            c;

        if (dockedItems) {
            while ((c = dockedItems.first())) {
                this.removeDocked(c, true);
            }
        }
    },

    doRenderDockedItems: function (out, renderData, after) {
        
        
        

        
        
        
        
        var me = renderData.$comp,
            layout = me.componentLayout,
            items,
            tree;

        if (layout.getDockedItems && !renderData.$skipDockedItems) {
            items = layout.getDockedItems('render', !after);
            tree = items && layout.getItemsRenderTree(items);

            if (tree) {
                Ext.DomHelper.generateMarkup(tree, out);
            }
        }
    },

    
    getDockedComponent: function(comp) {
        if (Ext.isObject(comp)) {
            comp = comp.getItemId();
        }
        return this.dockedItems.get(comp);
    },

    
    getDockedItems : function(selector, beforeBody) {
        var dockedItems = this.getComponentLayout().getDockedItems('render', beforeBody);

        if (selector && dockedItems.length) {
            dockedItems = Ext.ComponentQuery.query(selector, dockedItems);
        }

        return dockedItems;
    },

    getDockingRefItems: function(deep, containerItems) {
        
        var selector = deep && '*,* *',
            
            dockedItems = this.getDockedItems(selector, true),
            items;

        
        dockedItems.push.apply(dockedItems, containerItems);

        
        items = this.getDockedItems(selector, false);
        dockedItems.push.apply(dockedItems, items);

        return dockedItems;
    },

    initDockingItems: function() {
        var me = this,
            items = me.dockedItems;

        me.dockedItems = new Ext.util.AbstractMixedCollection(false, me.getComponentId);
        if (items) {
            me.addDocked(items);
        }
    },

    
    insertDocked : function(pos, items) {
        this.addDocked(items, pos);
    },

    
    
    onDockedAdd : Ext.emptyFn,
    
    onDockedRemove : Ext.emptyFn,

    
    removeDocked : function(item, autoDestroy) {
        var me = this,
            layout,
            hasLayout;

        autoDestroy = autoDestroy === true || (autoDestroy !== false && me.autoDestroy);
        if (!me.dockedItems.contains(item)) {
            return item;
        }
        if (item.dock === 'left' || item.dock === 'right') {
            me.horizontalDocks--;
        }

        layout = me.componentLayout;
        hasLayout = layout && me.rendered;

        if (hasLayout) {
            layout.onRemove(item);
        }

        me.dockedItems.remove(item);
        
        item.onRemoved(item.destroying || autoDestroy);
        me.onDockedRemove(item);

        if (autoDestroy) {
            item.destroy();
        } else if (hasLayout) {
            
            layout.afterRemove(item);
        }
        
        if (me.hasListeners.dockedremove) {
            me.fireEvent('dockedremove', me, item);
        }

        if (!me.destroying && !me.suspendLayout) {
            me.updateLayout();
        }

        return item;
    },

    setupDockingRenderTpl: function (renderTpl) {
        renderTpl.renderDockedItems = this.doRenderDockedItems;
    }
});


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


Ext.define('Ext.layout.container.boxOverflow.None', {
    alternateClassName: 'Ext.layout.boxOverflow.None',
    
    constructor: function(layout, config) {
        this.layout = layout;
        Ext.apply(this, config);
    },

    handleOverflow: Ext.emptyFn,

    clearOverflow: Ext.emptyFn,

    beginLayout: Ext.emptyFn,
    beginLayoutCycle: Ext.emptyFn,

    calculate: function(ownerContext) {
        var me = this,
            plan = ownerContext.state.boxPlan,
            overflow;

        if (plan && plan.tooNarrow) {
            overflow = me.handleOverflow(ownerContext);

            if (overflow) {
                if (overflow.reservedSpace) {
                    me.layout.publishInnerCtSize(ownerContext, overflow.reservedSpace);
                }

                
                
                
                
                
                
                
                
                
                
            }
        } else {
            me.clearOverflow();
        }
    },

    completeLayout: Ext.emptyFn,

    finishedLayout: function (ownerContext) {
        var me = this,
            owner = me.layout.owner,
            hiddens,
            hiddenCount;

        
        if (owner.hasListeners.overflowchange) {
            hiddens = owner.query('>[hidden]');
            hiddenCount = hiddens.length;
            if (hiddenCount !== me.lastHiddenCount) {
                owner.fireEvent('overflowchange', me.lastHiddenCount, hiddenCount, hiddens);
                me.lastHiddenCount = hiddenCount;
            }
        }
    },

    onRemove: Ext.emptyFn,

    
    getItem: function(item) {
        return this.layout.owner.getComponent(item);
    },
    
    getOwnerType: function(owner){
        var type;
        if (owner.isToolbar) {
            type = 'toolbar';
        } else if (owner.isTabBar) {
            type = 'tabbar';
        } else if (owner.isMenu) {
            type = 'menu';
        } else {
            type = owner.getXType();
        }
        
        return type;
    },

    getPrefixConfig: Ext.emptyFn,
    getSuffixConfig: Ext.emptyFn,
    getOverflowCls: function() {
        return '';
    }
});


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


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


Ext.define('Ext.button.Manager', {
    singleton: true,

    alternateClassName: 'Ext.ButtonToggleManager',

    groups: {},

    pressedButton: null,

    buttonSelector: '.' + Ext.baseCSSPrefix + 'btn',

    init: function() {
        var me = this;
        if (!me.initialized) {
            Ext.getDoc().on({
                keydown: me.onDocumentKeyDown,
                mouseup: me.onDocumentMouseUp,
                scope: me
            });
            me.initialized = true;
        }
    },

    
    
    onDocumentKeyDown: function(e) {
        var k = e.getKey(),
            btn;

        
        if (k === e.SPACE || k === e.ENTER) {

            
            btn = e.getTarget(this.buttonSelector);

            
            if (btn) {
                Ext.getCmp(btn.id).onClick(e);
            }
        }
    },

    
    
    
    onButtonMousedown: function(button, e) {
        var pressed = this.pressedButton;
        if (pressed) {
            pressed.onMouseUp(e);
        }
        this.pressedButton = button;
    },

    onDocumentMouseUp: function(e) {
        var pressed = this.pressedButton;
        
        if (pressed) {
            pressed.onMouseUp(e);
            this.pressedButton = null;
        }
    },

    toggleGroup: function(btn, state) {
        if (state) {
            var g = this.groups[btn.toggleGroup],
                length = g.length,
                i;

            for (i = 0; i < length; i++) {
                if (g[i] !== btn) {
                    g[i].toggle(false);
                }
            }
        }
    },

    register: function(btn) {
        var me = this,
            groups = this.groups,
            group = groups[btn.toggleGroup];

        me.init();
        if (!btn.toggleGroup) {
            return;
        }

        if (!group) {
            group = groups[btn.toggleGroup] = [];
        }
        group.push(btn);
        btn.on('toggle', me.toggleGroup, me);
    },

    unregister: function(btn) {
        if (!btn.toggleGroup) {
            return;
        }
        var me = this,
            group = me.groups[btn.toggleGroup];

        if (group) {
            Ext.Array.remove(group, btn);
            btn.un('toggle', me.toggleGroup, me);
        }
    },

    
    
    
    getPressed: function(group) {
        var g = this.groups[group],
            i = 0,
            len;

        if (g) {
            for (len = g.length; i < len; i++) {
                if (g[i].pressed === true) {
                    return g[i];
                }
            }
        }
        return null;
    } 
});


Ext.define('Ext.menu.Manager', {
    singleton: true,
               
                                   
                         
      
    alternateClassName: 'Ext.menu.MenuMgr',

                            
    
    menuSelector: '.' + Ext.baseCSSPrefix + 'menu',

    menus: {},
    groups: {},
    attached: false,
    lastShow: new Date(),

    init: function() {
        var me = this;
        
        me.active = new Ext.util.MixedCollection();
        Ext.getDoc().addKeyListener(27, function() {
            if (me.active.length > 0) {
                me.hideAll();
            }
        }, me);
    },

    
    hideAll: function() {
        var active = this.active,
            menus, m, mLen;

        if (active && active.length > 0) {
            menus = Ext.Array.slice(active.items);
            mLen  = menus.length;

            for (m = 0; m < mLen; m++) {
                menus[m].hide();
            }

            return true;
        }
        return false;
    },

    onHide: function(m) {
        var me = this,
            active = me.active;
        active.remove(m);
        if (active.length < 1) {
            Ext.getDoc().un('mousedown', me.onMouseDown, me);
            me.attached = false;
        }
    },

    onShow: function(m) {
        var me = this,
            active   = me.active,
            attached = me.attached;

        me.lastShow = new Date();
        active.add(m);
        if (!attached) {
            Ext.getDoc().on('mousedown', me.onMouseDown, me, {
                
                
                buffer: Ext.isIE9m ? 10 : undefined
            });
            me.attached = true;
        }
        m.toFront();
    },

    onBeforeHide: function(m) {
        if (m.activeChild) {
            m.activeChild.hide();
        }
        if (m.autoHideTimer) {
            clearTimeout(m.autoHideTimer);
            delete m.autoHideTimer;
        }
    },

    onBeforeShow: function(m) {
        var active = this.active,
            parentMenu = m.parentMenu;
            
        active.remove(m);
        if (!parentMenu && !m.allowOtherMenus) {
            this.hideAll();
        }
        else if (parentMenu && parentMenu.activeChild && m != parentMenu.activeChild) {
            parentMenu.activeChild.hide();
        }
    },

    
    onMouseDown: function(e) {
        var me = this,
            active = me.active,
            lastShow = me.lastShow,
            doHide = true;

        if (Ext.Date.getElapsed(lastShow) > 50 && active.length > 0 && !e.getTarget(me.menuSelector)) {
            
            
            
            if (Ext.isIE9m && !Ext.getDoc().contains(e.target)) {
                doHide = false;
            }
            if (doHide) {
                me.hideAll();
            }
        }
    },

    
    register: function(menu) {
        var me = this;

        if (!me.active) {
            me.init();
        }

        if (menu.floating) {
            me.menus[menu.id] = menu;
            menu.on({
                beforehide: me.onBeforeHide,
                hide: me.onHide,
                beforeshow: me.onBeforeShow,
                show: me.onShow,
                scope: me
            });
        }
    },

    
    get: function(menu) {
        var menus = this.menus;
        
        if (typeof menu == 'string') { 
            if (!menus) {  
                return null;
            }
            return menus[menu];
        } else if (menu.isMenu) {  
            return menu;
        } else if (Ext.isArray(menu)) { 
            return new Ext.menu.Menu({items:menu});
        } else { 
            return Ext.ComponentManager.create(menu, 'menu');
        }
    },

    
    unregister: function(menu) {
        var me = this,
            menus = me.menus,
            active = me.active;

        delete menus[menu.id];
        active.remove(menu);
        menu.un({
            beforehide: me.onBeforeHide,
            hide: me.onHide,
            beforeshow: me.onBeforeShow,
            show: me.onShow,
            scope: me
        });
    },

    
    registerCheckable: function(menuItem) {
        var groups  = this.groups,
            groupId = menuItem.group;

        if (groupId) {
            if (!groups[groupId]) {
                groups[groupId] = [];
            }

            groups[groupId].push(menuItem);
        }
    },

    
    unregisterCheckable: function(menuItem) {
        var groups  = this.groups,
            groupId = menuItem.group;

        if (groupId) {
            Ext.Array.remove(groups[groupId], menuItem);
        }
    },

    onCheckChange: function(menuItem, state) {
        var groups  = this.groups,
            groupId = menuItem.group,
            i       = 0,
            group, ln, curr;

        if (groupId && state) {
            group = groups[groupId];
            ln = group.length;
            for (; i < ln; i++) {
                curr = group[i];
                if (curr != menuItem) {
                    curr.setChecked(false);
                }
            }
        }
    }
});


Ext.define('Ext.util.ClickRepeater', {
    extend:  Ext.util.Observable ,

    
    constructor : function(el, config){
        var me = this;

        me.el = Ext.get(el);
        me.el.unselectable();

        Ext.apply(me, config);

        me.callParent();

        me.addEvents(
        
        "mousedown",
        
        "click",
        
        "mouseup"
        );

        if(!me.disabled){
            me.disabled = true;
            me.enable();
        }

        
        if(me.handler){
            me.on("click", me.handler,  me.scope || me);
        }
    },

    

    

    

    
    interval : 20,

    
    delay: 250,

    
    preventDefault : true,

    
    stopDefault : false,

    timer : 0,

    
    enable: function(){
        if(this.disabled){
            this.el.on('mousedown', this.handleMouseDown, this);
            
            
            if (Ext.isIE && !(Ext.isIE10p || (Ext.isStrict && Ext.isIE9))){
                this.el.on('dblclick', this.handleDblClick, this);
            }
            if(this.preventDefault || this.stopDefault){
                this.el.on('click', this.eventOptions, this);
            }
        }
        this.disabled = false;
    },

    
    disable: function( force){
        if(force || !this.disabled){
            clearTimeout(this.timer);
            if(this.pressedCls){
                this.el.removeCls(this.pressedCls);
            }
            Ext.getDoc().un('mouseup', this.handleMouseUp, this);
            this.el.removeAllListeners();
        }
        this.disabled = true;
    },

    
    setDisabled: function(disabled){
        this[disabled ? 'disable' : 'enable']();
    },

    eventOptions: function(e){
        if(this.preventDefault){
            e.preventDefault();
        }
        if(this.stopDefault){
            e.stopEvent();
        }
    },

    
    destroy : function() {
        this.disable(true);
        Ext.destroy(this.el);
        this.clearListeners();
    },

    handleDblClick : function(e){
        clearTimeout(this.timer);
        this.el.blur();

        this.fireEvent("mousedown", this, e);
        this.fireEvent("click", this, e);
    },

    
    handleMouseDown : function(e){
        clearTimeout(this.timer);
        this.el.blur();
        if(this.pressedCls){
            this.el.addCls(this.pressedCls);
        }
        this.mousedownTime = new Date();

        Ext.getDoc().on("mouseup", this.handleMouseUp, this);
        this.el.on("mouseout", this.handleMouseOut, this);

        this.fireEvent("mousedown", this, e);
        this.fireEvent("click", this, e);

        
        if (this.accelerate) {
            this.delay = 400;
        }

        
        
        e = new Ext.EventObjectImpl(e);

        this.timer =  Ext.defer(this.click, this.delay || this.interval, this, [e]);
    },

    
    click : function(e){
        this.fireEvent("click", this, e);
        this.timer =  Ext.defer(this.click, this.accelerate ?
            this.easeOutExpo(Ext.Date.getElapsed(this.mousedownTime),
                400,
                -390,
                12000) :
            this.interval, this, [e]);
    },

    easeOutExpo : function (t, b, c, d) {
        return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
    },

    
    handleMouseOut : function(){
        clearTimeout(this.timer);
        if(this.pressedCls){
            this.el.removeCls(this.pressedCls);
        }
        this.el.on("mouseover", this.handleMouseReturn, this);
    },

    
    handleMouseReturn : function(){
        this.el.un("mouseover", this.handleMouseReturn, this);
        if(this.pressedCls){
            this.el.addCls(this.pressedCls);
        }
        this.click();
    },

    
    handleMouseUp : function(e){
        clearTimeout(this.timer);
        this.el.un("mouseover", this.handleMouseReturn, this);
        this.el.un("mouseout", this.handleMouseOut, this);
        Ext.getDoc().un("mouseup", this.handleMouseUp, this);
        if(this.pressedCls){
            this.el.removeCls(this.pressedCls);
        }
        this.fireEvent("mouseup", this, e);
    }
});


Ext.define('Ext.layout.component.Button', {

    

    alias: ['layout.button'],

    extend:  Ext.layout.component.Auto ,

    

    type: 'button',

    htmlRE: /<.*>/,

    beginLayout: function(ownerContext) {
        var me = this,
            owner = me.owner,
            text = owner.text;

        me.callParent(arguments);
        ownerContext.btnWrapContext = ownerContext.getEl('btnWrap');
        ownerContext.btnElContext = ownerContext.getEl('btnEl');
        ownerContext.btnInnerElContext = ownerContext.getEl('btnInnerEl');
        ownerContext.btnIconElContext = ownerContext.getEl('btnIconEl');

        if (text && me.htmlRE.test(text)) {
            ownerContext.isHtmlText = true;
            
            
            
            
            owner.btnInnerEl.setStyle('line-height', 'normal');
            owner.btnInnerEl.setStyle('padding-top', '');
        }
    },

    beginLayoutCycle: function(ownerContext) {
        var owner = this.owner,
            lastWidthModel = this.lastWidthModel;

        this.callParent(arguments);

        if (lastWidthModel && !this.lastWidthModel.shrinkWrap &&
            ownerContext.widthModel.shrinkWrap) {
            
            owner.btnWrap.setStyle('height', '');
            owner.btnEl.setStyle('height', '');
            owner.btnInnerEl.setStyle('line-height', '');
        }
    },

    calculate: function(ownerContext) {
        var me = this,
            owner = me.owner,
            btnElContext = ownerContext.btnElContext,
            btnInnerElContext = ownerContext.btnInnerElContext,
            btnWrapContext = ownerContext.btnWrapContext,
            mmax = Math.max,
            ownerHeight, contentHeight, btnElHeight, innerElHeight;

        me.callParent(arguments);

        if (ownerContext.heightModel.shrinkWrap) {
            
            
            
            

            
            
            btnElHeight = owner.btnEl.getHeight();
            if (ownerContext.isHtmlText) {
                me.centerInnerEl(
                    ownerContext,
                    btnElHeight
                );
                me.ieCenterIcon(ownerContext, btnElHeight);
            }
        } else {
            
            
            ownerHeight = ownerContext.getProp('height');

            
            if (ownerHeight) {
                
                
                contentHeight = ownerHeight - ownerContext.getFrameInfo().height - ownerContext.getPaddingInfo().height;

                
                
                
                btnElHeight = contentHeight;
                if ((owner.menu || owner.split) && owner.arrowAlign === 'bottom') {
                    
                    
                    btnElHeight -= btnWrapContext.getPaddingInfo().bottom;
                }

                
                
                
                
                innerElHeight = btnElHeight;
                if ((owner.icon || owner.iconCls || owner.glyph) &&
                    (owner.iconAlign === 'top' || owner.iconAlign === 'bottom')) {
                    innerElHeight -= btnInnerElContext.getPaddingInfo().height;
                }

                btnWrapContext.setProp('height', mmax(0, contentHeight));
                btnElContext.setProp('height', mmax(0, btnElHeight));
                
                if (ownerContext.isHtmlText) {
                    
                    
                    me.centerInnerEl(ownerContext, btnElHeight);
                } else {
                    
                    
                    
                    btnInnerElContext.setProp('line-height', mmax(0, innerElHeight) + 'px');
                }
                me.ieCenterIcon(ownerContext, btnElHeight);
            } else if (ownerHeight !== 0) {
                
                me.done = false;
            }
        }
    },

    centerInnerEl: function(ownerContext, btnElHeight) {
        var me = this,
            btnInnerElContext = ownerContext.btnInnerElContext,
            innerElHeight = me.owner.btnInnerEl.getHeight();

        if (ownerContext.heightModel.shrinkWrap && (btnElHeight < innerElHeight)) {
            
            
            
            ownerContext.btnElContext.setHeight(innerElHeight);
        } else if (btnElHeight > innerElHeight) {
            
            
            
            btnInnerElContext.setProp(
                'padding-top', 
                Math.round((btnElHeight - innerElHeight) / 2) +
                    
                    
                    
                    btnInnerElContext.getPaddingInfo().top 
            );
        }
    },

    ieCenterIcon: function(ownerContext, btnElHeight) {
        var iconAlign = this.owner.iconAlign;

        if ((Ext.isIEQuirks || Ext.isIE6) &&
            (iconAlign === 'left' || iconAlign === 'right')) {
            
            
            
            
            
            
            ownerContext.btnIconElContext.setHeight(btnElHeight);
        }
    },

    publishInnerWidth: function(ownerContext, width) {
        if (this.owner.getFrameInfo().table) {
            
            
            
            ownerContext.btnInnerElContext.setWidth(
                width -
                
                ownerContext.getFrameInfo().width - ownerContext.getPaddingInfo().width -
                
                
                ownerContext.btnWrapContext.getPaddingInfo().width
            );
        }
    }

});


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({
                role: 'presentation',
                cls: Ext.baseCSSPrefix + 'textmetrics'
            });
            
        me.measure = measure; 
        if (bindTo) {
            me.bind(bindTo);
        }
        
        measure.position('absolute');
        measure.setLocalXY(-1000, -1000);
        measure.hide();

        if (fixedWidth) {
           measure.setWidth(fixedWidth);
        }
    },
    
    
    getSize: function(text){
        var measure = this.measure,
            size;
        
        measure.update(text);
        size = measure.getSize();
        measure.update('');
        return size;
    },
    
    
    bind: function(el){
        var me = this;
        
        me.el = Ext.get(el);
        me.measure.setStyle(
            me.el.getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
        );
    },
    
    
     setFixedWidth : function(width){
         this.measure.setWidth(width);
     },
     
     
     getWidth : function(text){
         this.measure.dom.style.width = 'auto';
         return this.getSize(text).width;
     },
     
     
     getHeight : function(text){
         return this.getSize(text).height;
     },
     
     
     destroy: function(){
         var me = this;
         me.measure.remove();
         delete me.el;
         delete me.measure;
     }
}, function(){
    Ext.Element.addMethods({
        
        getTextWidth : function(text, min, max){
            return Ext.Number.constrain(Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width, min || 0, max || 1000000);
        }
    });
});


Ext.define('Ext.button.Button', {

    
    alias: 'widget.button',
    extend:  Ext.Component ,

               
                             
                           
                                 
                                      
                               
                         
      
    
    mixins: {
        queryable:  Ext.Queryable 
    },

    alternateClassName: 'Ext.Button',
    

    
    isButton: true,
    componentLayout: 'button',

    
    hidden: false,

    
    disabled: false,

    
    pressed: false,

    

    

    

    

    

    

    

    

    

    

    
    tabIndex: 0,

    

    
    enableToggle: false,

    

    

    
    menuAlign: 'tl-bl?',

    
    showEmptyMenu: false,

    
    textAlign: 'center',

    

    

    

    
    clickEvent: 'click',

    
    preventDefault: true,

    
    handleMouseEvents: true,

    
    tooltipType: 'qtip',

    
    baseCls: Ext.baseCSSPrefix + 'btn',

    
    pressedCls: 'pressed',

    
    overCls: 'over',

    
    focusCls: 'focus',

    
    menuActiveCls: 'menu-active',

    

    
     hrefTarget: '_blank',

     

    

    
    
    ariaRole: 'button',

    childEls: [
        'btnEl', 'btnWrap', 'btnInnerEl', 'btnIconEl'
    ],

    
    
    
    renderTpl: [
        '<span id="{id}-btnWrap" role="presentation" class="{baseCls}-wrap',
            '<tpl if="splitCls"> {splitCls}</tpl>',
            '{childElCls}" unselectable="on">',
            '<span id="{id}-btnEl" class="{baseCls}-button" role="presentation">',
                '<span id="{id}-btnInnerEl" class="{baseCls}-inner {innerCls}',
                    '{childElCls}" unselectable="on">',
                    '{text}',
                '</span>',
                '<span role="presentation" id="{id}-btnIconEl" class="{baseCls}-icon-el {iconCls}',
                    '{childElCls} {glyphCls}" unselectable="on" style="',
                    '<tpl if="iconUrl">background-image:url({iconUrl});</tpl>',
                    '<tpl if="glyph && glyphFontFamily">font-family:{glyphFontFamily};</tpl>">',
                    '<tpl if="glyph">&#{glyph};</tpl><tpl if="iconCls || iconUrl">&#160;</tpl>',
                '</span>',
            '</span>',
        '</span>',
        
        '<tpl if="closable">',
            '<span id="{id}-closeEl" role="presentation"',
                ' class="{baseCls}-close-btn"',
                '<tpl if="closeText">',
                    ' title="{closeText}" aria-label="{closeText}"',
                '</tpl>',
                '>',
            '</span>',
        '</tpl>'
    ],

    
    scale: 'small',

    
    allowedScales: ['small', 'medium', 'large'],

    

    
    iconAlign: 'left',

    
    arrowAlign: 'right',

    
    arrowCls: 'arrow',

    

    

    

    maskOnDisable: false,

    shrinkWrap: 3,

    frame: true,

    hasFrameTable: function () {
        
        
        return this.href && this.frameTable;
    },

    frameTableListener: function () {
        if (!this.disabled) {
            this.doNavigate();
        }
    },

    doNavigate: function () {
        
        
        if (this.hrefTarget === '_blank') {
            window.open(this.href, this.hrefTarget);
        } else {
            location.href = this.href;
        }
    },

    
    _triggerRegion: {},

    
    initComponent: function() {
        var me = this;

        
        
        me.autoEl = {
            tag: 'a',
            hidefocus: 'on',
            unselectable: 'on'
        };

        
        me.addCls(Ext.baseCSSPrefix + 'unselectable');

        me.callParent(arguments);

        me.addEvents(
            
            'click',

            
            'toggle',

            
            'mouseover',

            
            'mouseout',

            
            'menushow',

            
            'menuhide',

            
            'menutriggerover',

            
            'menutriggerout',

            
            'textchange',

            
            'iconchange',

            
            'glyphchange'
        );

        if (me.menu) {
            
            me.split = true;

            
            me.menu = Ext.menu.Manager.get(me.menu);

            
            
            me.menu.ownerButton = me;
        }

        
        if (me.url) {
            me.href = me.url;
        }

        
        if (me.href && !me.hasOwnProperty('preventDefault')) {
            me.preventDefault = false;
        }

        if (Ext.isString(me.toggleGroup) && me.toggleGroup !== '') {
            me.enableToggle = true;
        }

        if (me.html && !me.text) {
            me.text = me.html;
            delete me.html;
        }

        me.glyphCls = me.baseCls + '-glyph';
    },

    
    getActionEl: function() {
        return this.el;
    },

    
    getFocusEl: function() {
        return this.el;
    },

    
    onDisable: function(){
        this.callParent(arguments);
    },

    
    setComponentCls: function() {
        var me = this,
            cls = me.getComponentCls();

        if (!Ext.isEmpty(me.oldCls)) {
            me.removeClsWithUI(me.oldCls);
            me.removeClsWithUI(me.pressedCls);
        }

        me.oldCls = cls;
        me.addClsWithUI(cls);
    },

    getComponentCls: function() {
        var me = this,
            cls;

        
        if (me.iconCls || me.icon || me.glyph) {
            cls = [me.text ? 'icon-text-' + me.iconAlign : 'icon'];
        } else if (me.text) {
            cls = ['noicon'];
        } else {
            cls = [];
        }

        if (me.pressed) {
            cls[cls.length] = me.pressedCls;
        }
        return cls;
    },

    beforeRender: function () {
        var me = this,
            autoEl = me.autoEl,
            href = me.getHref(),
            hrefTarget = me.hrefTarget;

        if (!me.disabled) {
            autoEl.tabIndex = me.tabIndex;
        }

        if (href) {
            autoEl.href = href;
            if (hrefTarget) {
                autoEl.target = hrefTarget;
            }
        }

        me.callParent();

        
        me.oldCls = me.getComponentCls();
        me.addClsWithUI(me.oldCls);

        
        Ext.applyIf(me.renderData, me.getTemplateArgs());
    },

    
    onRender: function() {
        var me = this,
            addOnclick,
            btn,
            btnListeners;

        me.doc = Ext.getDoc();
        me.callParent(arguments);

        
        btn = me.el;

        if (me.tooltip) {
            me.setTooltip(me.tooltip, true);
        }

        
        if (me.handleMouseEvents) {
            btnListeners = {
                scope: me,
                mouseover: me.onMouseOver,
                mouseout: me.onMouseOut,
                mousedown: me.onMouseDown
            };
            if (me.split) {
                btnListeners.mousemove = me.onMouseMove;
            }
        } else {
            btnListeners = {
                scope: me
            };
        }

        
        if (me.menu) {
            me.mon(me.menu, {
                scope: me,
                show: me.onMenuShow,
                hide: me.onMenuHide
            });

            me.keyMap = new Ext.util.KeyMap({
                target: me.el,
                key: Ext.EventObject.DOWN,
                handler: me.onDownKey,
                scope: me
            });
        }

        
        if (me.repeat) {
            me.mon(new Ext.util.ClickRepeater(btn, Ext.isObject(me.repeat) ? me.repeat: {}), 'click', me.onRepeatClick, me);
        } else {

            
            if (btnListeners[me.clickEvent]) {
                addOnclick = true;
            } else {
                btnListeners[me.clickEvent] = me.onClick;
            }
        }

        
        me.mon(btn, btnListeners);

        if (me.hasFrameTable()) {
            me.mon(me.frameTable, 'click', me.frameTableListener, me);
        }

        
        if (addOnclick) {
            me.mon(btn, me.clickEvent, me.onClick, me);
        }

        Ext.button.Manager.register(me);
    },

    
    getTemplateArgs: function() {
        var me = this,
            glyph = me.glyph,
            glyphFontFamily = Ext._glyphFontFamily,
            glyphParts;

        if (typeof glyph === 'string') {
            glyphParts = glyph.split('@');
            glyph = glyphParts[0];
            glyphFontFamily = glyphParts[1];
        }

        return {
            innerCls : me.getInnerCls(),
            splitCls : me.getSplitCls(),
            iconUrl  : me.icon,
            iconCls  : me.iconCls,
            glyph: glyph,
            glyphCls: glyph ? me.glyphCls : '', 
            glyphFontFamily: glyphFontFamily,
            text     : me.text || '&#160;',
            closeText: me.closeText
        };
    },

    
    setHref: function(href) {
        this.href = href;
        this.el.dom.href = this.getHref();
    },

    
    getHref: function() {
        var me = this,
            href = me.href;

        return href ? Ext.urlAppend(href, Ext.Object.toQueryString(Ext.apply({}, me.params, me.baseParams))) : false;
    },

    
    setParams: function(params) {
        this.params = params;
        this.el.dom.href = this.getHref();
    },

    getSplitCls: function() {
        var me = this;
        return me.split ? (me.baseCls + '-' + me.arrowCls) + ' ' + (me.baseCls + '-' + me.arrowCls + '-' + me.arrowAlign) : '';
    },

    getInnerCls: function() {
        return this.textAlign ? this.baseCls + '-inner-' + this.textAlign : '';
    },

    
    setIcon: function(icon) {
        icon = icon || '';
        var me = this,
            btnIconEl = me.btnIconEl,
            oldIcon = me.icon || '';

        me.icon = icon;
        if (icon != oldIcon) {
            if (btnIconEl) {
                btnIconEl.setStyle('background-image', icon ? 'url(' + icon + ')': '');
                me.setComponentCls();
                if (me.didIconStateChange(oldIcon, icon)) {
                    me.updateLayout();
                }
            }
            me.fireEvent('iconchange', me, oldIcon, icon);
        }
        return me;
    },

    
    setIconCls: function(cls) {
        cls = cls || '';
        var me = this,
            btnIconEl = me.btnIconEl,
            oldCls = me.iconCls || '';

        me.iconCls = cls;
        if (oldCls != cls) {
            if (btnIconEl) {
                
                btnIconEl.removeCls(oldCls);
                btnIconEl.addCls(cls);
                me.setComponentCls();
                if (me.didIconStateChange(oldCls, cls)) {
                    me.updateLayout();
                }
            }
            me.fireEvent('iconchange', me, oldCls, cls);
        }
        return me;
    },

    
    setGlyph: function(glyph) {
        glyph = glyph || 0;
        var me = this,
            btnIconEl = me.btnIconEl,
            oldGlyph = me.glyph,
            fontFamily, glyphParts;

        me.glyph = glyph;

        if (btnIconEl) {
            if (typeof glyph === 'string') {
                glyphParts = glyph.split('@');
                glyph = glyphParts[0];
                fontFamily = glyphParts[1] || Ext._glyphFontFamily;
            }

            if (!glyph) {
                btnIconEl.dom.innerHTML = '';
            } else if (oldGlyph != glyph) {
                btnIconEl.dom.innerHTML = '&#' + glyph + ';';
            }

            if (fontFamily) {
                btnIconEl.setStyle('font-family', fontFamily);
            }
        }

        me.fireEvent('glyphchange', me, me.glyph, oldGlyph);

        return me;
    },

    
    setTooltip: function(tooltip, initial) {
        var me = this;

        if (me.rendered) {
            if (!initial || !tooltip) {
                me.clearTip();
            }
            if (tooltip) {
                if (Ext.quickTipsActive && Ext.isObject(tooltip)) {
                    Ext.tip.QuickTipManager.register(Ext.apply({
                        target: me.el.id
                    },
                    tooltip));
                    me.tooltip = tooltip;
                } else {
                    me.el.dom.setAttribute(me.getTipAttr(), tooltip);
                }
            }
        } else {
            me.tooltip = tooltip;
        }
        return me;
    },

    
    setTextAlign: function(align) {
        var me = this,
            btnEl = me.btnEl;

        if (btnEl) {
            btnEl.removeCls(me.baseCls + '-inner-' + me.textAlign);
            btnEl.addCls(me.baseCls + '-inner-' + align);
        }
        me.textAlign = align;
        return me;
    },

    getTipAttr: function(){
        return this.tooltipType == 'qtip' ? 'data-qtip' : 'title';
    },

    
    getRefItems: function(deep){
        var menu = this.menu,
            items;

        if (menu) {
            items = menu.getRefItems(deep);
            items.unshift(menu);
        }
        return items || [];
    },

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

        if (Ext.quickTipsActive && Ext.isObject(me.tooltip)) {
            Ext.tip.QuickTipManager.unregister(el);
        } else {
            el.dom.removeAttribute(me.getTipAttr());
        }
    },

    
    beforeDestroy: function() {
        var me = this;
        if (me.rendered) {
            me.clearTip();
        }
        if (me.menu && me.destroyMenu !== false) {
            Ext.destroy(me.menu);
        }
        Ext.destroy(me.btnInnerEl, me.repeater);
        me.callParent();
    },

    
    onDestroy: function() {
        var me = this;
        if (me.rendered) {
            me.doc.un('mouseover', me.monitorMouseOver, me);
            delete me.doc;

            Ext.destroy(me.keyMap);
            delete me.keyMap;
        }
        Ext.button.Manager.unregister(me);
        me.callParent();
    },

    
    setHandler: function(handler, scope) {
        this.handler = handler;
        this.scope = scope;
        return this;
    },

    
    setText: function(text) {
        text = text || '';
        var me = this,
            oldText = me.text || '';

        if (text != oldText) {
            me.text = text;
            if (me.rendered) {
                me.btnInnerEl.update(text || '&#160;');
                me.setComponentCls();
                if (Ext.isStrict && Ext.isIE8) {
                    
                    me.el.repaint();
                }
                me.updateLayout();
            }
            me.fireEvent('textchange', me, oldText, text);
        }
        return me;
    },

    
    didIconStateChange: function(old, current) {
        var currentEmpty = Ext.isEmpty(current);
        return Ext.isEmpty(old) ? !currentEmpty : currentEmpty;
    },

    
    getText: function() {
        return this.text;
    },

    
    toggle: function(state, suppressEvent) {
        var me = this;
        state = state === undefined ? !me.pressed: !!state;
        if (state !== me.pressed) {
            if (me.rendered) {
                me[state ? 'addClsWithUI': 'removeClsWithUI'](me.pressedCls);
            }
            me.pressed = state;
            if (!suppressEvent) {
                me.fireEvent('toggle', me, state);
                Ext.callback(me.toggleHandler, me.scope || me, [me, state]);
            }
        }
        return me;
    },

    maybeShowMenu: function(){
        var me = this;
        if (me.menu && !me.hasVisibleMenu() && !me.ignoreNextClick) {
            me.showMenu(true);
        }
    },

    
    showMenu: function( fromEvent) {
        var me = this,
            menu = me.menu;

        if (me.rendered) {
            if (me.tooltip && Ext.quickTipsActive && me.getTipAttr() != 'title') {
                Ext.tip.QuickTipManager.getQuickTip().cancelShow(me.el);
            }
            if (menu.isVisible()) {
                menu.hide();
            }

            if (!fromEvent || me.showEmptyMenu || menu.items.getCount() > 0) {
                menu.showBy(me.el, me.menuAlign);
            }
        }
        return me;
    },

    
    hideMenu: function() {
        if (this.hasVisibleMenu()) {
            this.menu.hide();
        }
        return this;
    },

    
    hasVisibleMenu: function() {
        var menu = this.menu;
        return menu && menu.rendered && menu.isVisible();
    },

    
    onRepeatClick: function(repeat, e) {
        this.onClick(e);
    },

    
    onClick: function(e) {
        var me = this;
        me.doPreventDefault(e);

        
        
        if (e.type !== 'keydown' && e.button !== 0) {
            return;
        }
        if (!me.disabled) {
            me.doToggle();
            me.maybeShowMenu();
            me.fireHandler(e);
        }
    },
    
    doPreventDefault: function(e) {
        if (this.preventDefault || (this.disabled && this.getHref()) && e) {
            e.preventDefault();
        }
    },

    fireHandler: function(e) {
        var me = this,
            handler = me.handler;

        if (me.fireEvent('click', me, e) !== false) {
            if (handler) {
                handler.call(me.scope || me, me, e);
            }
        }
    },

    doToggle: function() {
        var me = this;    
        if (me.enableToggle && (me.allowDepress !== false || !me.pressed)) {
            me.toggle();
        }
    },

    
    onMouseOver: function(e) {
        var me = this;
        if (!me.disabled && !e.within(me.el, true, true)) {
            me.onMouseEnter(e);
        }
    },

    
    onMouseOut: function(e) {
        var me = this;
        if (!e.within(me.el, true, true)) {
            if (me.overMenuTrigger) {
                me.onMenuTriggerOut(e);
            }
            me.onMouseLeave(e);
        }
    },

    
    onMouseMove: function(e) {
        var me = this,
            el = me.el,
            over = me.overMenuTrigger,
            overPosition, triggerRegion;

        if (me.split) {
            overPosition = (me.arrowAlign === 'right') ?
                e.getX() - me.getX() : e.getY() - el.getY();
            triggerRegion = me.getTriggerRegion();

            if (overPosition > triggerRegion.begin && overPosition < triggerRegion.end) {
                if (!over) {
                    me.onMenuTriggerOver(e);
                }
            } else {
                if (over) {
                    me.onMenuTriggerOut(e);
                }
            }
        }
    },

    
    getTriggerRegion: function() {
        var me = this,
            region = me._triggerRegion,
            triggerSize = me.getTriggerSize(),
            btnSize = me.arrowAlign === 'right' ? me.getWidth() : me.getHeight();

        region.begin = btnSize - triggerSize;
        region.end = btnSize;
        return region;
    },

    
    getTriggerSize: function() {
        var me = this,
            size = me.triggerSize,
            side, sideFirstLetter;

        if (size == null) { 
            side = me.arrowAlign;
            sideFirstLetter = side.charAt(0);
            size = me.triggerSize = me.el.getFrameWidth(sideFirstLetter) + me.getBtnWrapFrameWidth(sideFirstLetter)
            if (me.frameSize) {
                size = me.triggerSize += me.frameSize[side];
            }
        }
        return size;
    },

    
    getBtnWrapFrameWidth: function(side) {
        return this.btnWrap.getFrameWidth(side);
    },

    addOverCls: function() {
        if (!this.disabled) {
            this.addClsWithUI(this.overCls);
        }
    },
    removeOverCls: function() {
        this.removeClsWithUI(this.overCls);
    },

    
    onMouseEnter: function(e) {
        
        this.fireEvent('mouseover', this, e);
    },

    
    onMouseLeave: function(e) {
        
        this.fireEvent('mouseout', this, e);
    },

    
    onMenuTriggerOver: function(e) {
        var me = this,
            arrowTip = me.arrowTooltip;

        me.overMenuTrigger = true;
        
        
        if (me.split && arrowTip) {
            me.btnWrap.dom.setAttribute(me.getTipAttr(), arrowTip);
        }
        me.fireEvent('menutriggerover', me, me.menu, e);
    },

    
    onMenuTriggerOut: function(e) {
        var me = this;
        delete me.overMenuTrigger;
        
        if (me.split && me.arrowTooltip) {
            me.btnWrap.dom.setAttribute(me.getTipAttr(), '');
        }
        me.fireEvent('menutriggerout', me, me.menu, e);
    },

    
    enable: function(silent) {
        var me = this;

        me.callParent(arguments);

        me.removeClsWithUI('disabled');
        if (me.rendered) {
            me.el.dom.setAttribute('tabIndex', me.tabIndex);
        }

        return me;
    },

    
    disable: function(silent) {
        var me = this;

        me.callParent(arguments);

        me.addClsWithUI('disabled');
        me.removeClsWithUI(me.overCls);
        if (me.rendered) {
            me.el.dom.removeAttribute('tabIndex');
        }

        
        
        if (me.btnInnerEl && Ext.isIE7m) {
            me.btnInnerEl.repaint();
        }

        return me;
    },

    
    setScale: function(scale) {
        var me = this,
            ui = me.ui.replace('-' + me.scale, '');

        
        if (!Ext.Array.contains(me.allowedScales, scale)) {
            throw('#setScale: scale must be an allowed scale (' + me.allowedScales.join(', ') + ')');
        }

        me.scale = scale;
        me.setUI(ui);
    },

    
    setUI: function(ui) {
        var me = this;

        
        if (me.scale && !ui.match(me.scale)) {
            ui = ui + '-' + me.scale;
        }

        me.callParent([ui]);

        
        
    },


    
    onMouseDown: function(e) {
        var me = this;

        if (Ext.isIE) {
            
            
            me.getFocusEl().focus();
        }

        if (!me.disabled && e.button === 0) {
            Ext.button.Manager.onButtonMousedown(me, e);
            me.addClsWithUI(me.pressedCls);
        }
    },
    
    onMouseUp: function(e) {
        var me = this;

        
        if (!me.isDestroyed && e.button === 0) {
            if (!me.pressed) {
                me.removeClsWithUI(me.pressedCls);
            }
        }
    },
    
    onMenuShow: function(e) {
        var me = this;
        me.ignoreNextClick = 0;
        me.addClsWithUI(me.menuActiveCls);
        me.fireEvent('menushow', me, me.menu);
    },

    
    onMenuHide: function(e) {
        var me = this;
        me.removeClsWithUI(me.menuActiveCls);
        me.ignoreNextClick = Ext.defer(me.restoreClick, 250, me);
        me.fireEvent('menuhide', me, me.menu);
        me.focus();
    },

    
    restoreClick: function() {
        this.ignoreNextClick = 0;
    },

    
    onDownKey: function(k, e) {
        var me = this;

        if (me.menu && !me.disabled) {
            me.showMenu();
            e.stopEvent();
            return false;
        }
    }
});


Ext.define('Ext.layout.container.boxOverflow.Menu', {

    

    extend:  Ext.layout.container.boxOverflow.None ,
                                                             
    alternateClassName: 'Ext.layout.boxOverflow.Menu',
    
    

    

    
    noItemsMenuText : '<div class="' + Ext.baseCSSPrefix + 'toolbar-no-items" role="menuitem">(None)</div>',

    constructor: function(layout) {
        var me = this;

        me.callParent(arguments);

        me.triggerButtonCls = me.triggerButtonCls || Ext.baseCSSPrefix + 'box-menu-after';
        
        me.menuItems = [];
    },

    beginLayout: function (ownerContext) {
        this.callParent(arguments);

        
        
        this.clearOverflow(ownerContext);
    },

    beginLayoutCycle: function (ownerContext, firstCycle) {
        this.callParent(arguments);

        if (!firstCycle) {
            
            
            this.clearOverflow(ownerContext);

            this.layout.cacheChildItems(ownerContext);
        }
    },

    onRemove: function(comp){
        Ext.Array.remove(this.menuItems, comp);
    },

    
    getSuffixConfig: function() {
        var me = this,
            layout = me.layout,
            owner = layout.owner,
            oid = owner.id;

        
        me.menu = new Ext.menu.Menu({
            listeners: {
                scope: me,
                beforeshow: me.beforeMenuShow
            }
        });

        
        me.menuTrigger = new Ext.button.Button({
            id: oid + '-menu-trigger',
            cls: Ext.layout.container.Box.prototype.innerCls + ' ' + me.triggerButtonCls + ' ' + Ext.baseCSSPrefix + 'toolbar-item',
            plain: owner.usePlainButtons,
            ownerCt: owner, 
            ownerLayout: layout,
            iconCls: Ext.baseCSSPrefix + me.getOwnerType(owner) + '-more-icon',
            ui: owner instanceof Ext.toolbar.Toolbar ? 'default-toolbar' : 'default',
            menu: me.menu,
            
            showEmptyMenu: true,
            getSplitCls: function() { return '';}
        });

        return me.menuTrigger.getRenderTree();
    },
    
    getOverflowCls: function() {
        return Ext.baseCSSPrefix + this.layout.direction + '-box-overflow-body';
    },

    handleOverflow: function(ownerContext) {
        var me = this,
            layout = me.layout,
            names = layout.names,
            plan = ownerContext.state.boxPlan,
            posArgs = [null, null];

        me.showTrigger(ownerContext);

        
        
        if (me.layout.direction !== 'vertical') {
            posArgs[names.heightIndex] = (plan.maxSize - me.menuTrigger[names.getHeight]()) / 2;
            me.menuTrigger.setPosition.apply(me.menuTrigger, posArgs);
        }

        return {
            reservedSpace: me.triggerTotalWidth
        };
    },

    
    captureChildElements: function() {
        var me = this,
            menuTrigger = me.menuTrigger,
            names = me.layout.names;

        
        if (menuTrigger.rendering) {
            menuTrigger.finishRender();
            me.triggerTotalWidth = menuTrigger[names.getWidth]() + menuTrigger.el.getMargin(names.parallelMargins);
        }
    },

    _asLayoutRoot: { isRoot: true },

    
    clearOverflow: function(ownerContext) {
        var me = this,
            items = me.menuItems,
            item,
            i = 0,
            length = items.length,
            owner = me.layout.owner,
            asLayoutRoot = me._asLayoutRoot;

        owner.suspendLayouts();
        me.captureChildElements();
        me.hideTrigger();
        owner.resumeLayouts();

        for (; i < length; i++) {
            item = items[i];

            
            
            item.suspendLayouts();
            item.show();
            item.resumeLayouts(asLayoutRoot);
        }

        items.length = 0;
    },

    
    showTrigger: function(ownerContext) {
        var me = this,
            layout = me.layout,
            owner = layout.owner,
            names = layout.names,
            startProp = names.x,
            sizeProp = names.width,
            plan = ownerContext.state.boxPlan,
            available = plan.targetSize[sizeProp],
            childItems = ownerContext.childItems,
            len = childItems.length,
            menuTrigger = me.menuTrigger,
            childContext,
            comp, i, props;

        
        
        menuTrigger.suspendLayouts();
        menuTrigger.show();
        menuTrigger.resumeLayouts(me._asLayoutRoot);

        available -= me.triggerTotalWidth;

        owner.suspendLayouts();

        
        
        me.menuItems.length = 0;
        for (i = 0; i < len; i++) {
            childContext = childItems[i];
            props = childContext.props;
            if (props[startProp] + props[sizeProp] > available) {
                comp = childContext.target;
                me.menuItems.push(comp);
                comp.hide();
            }
        }

        owner.resumeLayouts();
    },

    
    hideTrigger: function() {
        var menuTrigger = this.menuTrigger;
        if (menuTrigger) {
            menuTrigger.hide();
        }
    },

    
    beforeMenuShow: function(menu) {
        var me = this,
            items = me.menuItems,
            i = 0,
            len   = items.length,
            item,
            prev,
            needsSep = function(group, prev){
                return group.isXType('buttongroup') && !(prev instanceof Ext.toolbar.Separator);
            };

        menu.suspendLayouts();
        me.clearMenu();
        menu.removeAll();

        for (; i < len; i++) {
            item = items[i];

            
            if (!i && (item instanceof Ext.toolbar.Separator)) {
                continue;
            }
            if (prev && (needsSep(item, prev) || needsSep(prev, item))) {
                menu.add('-');
            }

            me.addComponentToMenu(menu, item);
            prev = item;
        }

        
        if (menu.items.length < 1) {
            menu.add(me.noItemsMenuText);
        }
        menu.resumeLayouts();
    },
    
    
    createMenuConfig : function(component, hideOnClick) {
        var me = this,
            config = Ext.apply({}, component.initialConfig),
            group  = component.toggleGroup;

        Ext.copyTo(config, component, [
            'iconCls', 'icon', 'itemId', 'disabled', 'handler', 'scope', 'menu', 'tabIndex'
        ]);

        Ext.apply(config, {
            text       : component.overflowText || component.text,
            hideOnClick: hideOnClick,
            destroyMenu: false,
            listeners  : {}
        });

        
        if (component.isFormField) {
            config.value = component.getValue();

            
            
            
            
            
            config.listeners.change = function(c, newVal, oldVal) {                            
                component.setValue(newVal);
            }
        }

        
        else if (group || component.enableToggle) {
            Ext.apply(config, {
                hideOnClick: false,
                group  : group,
                checked: component.pressed,
                handler: function(item, e) {
                    component.onClick(e);
                }
            });
        }

        
        if (component.isButton && !component.changeListenersAdded) {
            component.on({
                textchange: me.onButtonAttrChange,
                iconchange: me.onButtonAttrChange,
                toggle:     me.onButtonToggle
            });
            component.changeListenersAdded = true;
        }

        
        
        
        delete config.margin;
        delete config.ownerCt;
        delete config.xtype;
        delete config.id;
        delete config.itemId;
        return config;
    },

    onButtonAttrChange: function(btn) {
        var clone = btn.overflowClone;
        clone.suspendLayouts();
        clone.setText(btn.text);
        clone.setIcon(btn.icon);
        clone.setIconCls(btn.iconCls);
        clone.resumeLayouts(true);
    },

    onButtonToggle: function(btn, state) {
        
        if (btn.overflowClone.checked !== state) {
            btn.overflowClone.setChecked(state);
        }
    },

    
    addComponentToMenu : function(menu, component) {
        var me = this,
        i, items, iLen;

        if (component instanceof Ext.toolbar.Separator) {
            menu.add('-');
        } else if (component.isComponent) {
            if (component.isXType('splitbutton')) {
                component.overflowClone = menu.add(me.createMenuConfig(component, true));

            } else if (component.isXType('button')) {
                component.overflowClone = menu.add(me.createMenuConfig(component, !component.menu));

            } else if (component.isXType('buttongroup')) {
                items = component.items.items;
                iLen  = items.length;

                for (i = 0; i < iLen; i++) {
                    me.addComponentToMenu(menu, items[i]);
                }
            } else {
                component.overflowClone = menu.add(Ext.create(Ext.getClassName(component), me.createMenuConfig(component)));
            }
        }
    },

    
    clearMenu : function() {
        var menu = this.menu,
            items, i, iLen, item;
        
        if (menu && menu.items) {
            items = menu.items.items;
            iLen  = items.length;
            
            for (i = 0; i < iLen; i++) {
                item = items[i];
                if (item.setMenu) {
                    item.setMenu(null);
                }
            }
        }
    },

    
    destroy: function() {
        var trigger = this.menuTrigger;
            
        if (trigger && !this.layout.owner.items.contains(trigger)) {
            
            
            delete trigger.ownerCt;
        }
        Ext.destroy(this.menu, trigger);
    }
});


Ext.define('Ext.layout.container.boxOverflow.Scroller', {

    

    extend:  Ext.layout.container.boxOverflow.None ,
                                                        
    alternateClassName: 'Ext.layout.boxOverflow.Scroller',
    mixins: {
        observable:  Ext.util.Observable 
    },
    
    

    
    animateScroll: false,

    
    scrollIncrement: 20,

    
    wheelIncrement: 10,

    
    scrollRepeatInterval: 60,

    
    scrollDuration: 400,

    

    

    
    scrollerCls: Ext.baseCSSPrefix + 'box-scroller',

    

    

    constructor: function(layout, config) {
        var me = this;

        me.layout = layout;
        Ext.apply(me, config || {});

        
        me.mixins.observable.constructor.call(me);

        me.addEvents(
            
            'scroll'
        );
        me.scrollPosition = 0;
        me.scrollSize = 0;
    },

    getPrefixConfig: function() {
        var me = this,
            layout = me.layout,
            owner = layout.owner,
            cls;

        me.initCSSClasses();
        cls = Ext.layout.container.Box.prototype.innerCls + ' ' + me.beforeCtCls;
        if (owner.plain) {
            
            cls += ' ' + me.scrollerCls + '-plain';
        }
        return {
            role: 'presentation',
            cls: cls,
            cn : {
                role: 'presentation',
                id : owner.id + layout.names.beforeScrollerSuffix,
                cls: me.scrollerCls + ' ' + me.beforeScrollerCls,
                style: 'display:none'
            }
        };
    },

    getSuffixConfig: function() {
        var me = this,
            layout = me.layout,
            owner = layout.owner,
            cls = Ext.layout.container.Box.prototype.innerCls + ' ' + me.afterCtCls;

        if (owner.plain) {
            
            cls += ' ' + me.scrollerCls + '-plain';
        }
        return {
            role: 'presentation',
            cls: cls,
            cn : {
                role: 'presentation',
                id : owner.id + layout.names.afterScrollerSuffix,
                cls: me.scrollerCls + ' ' + me.afterScrollerCls,
                style: 'display:none'
            }
        };
    },

    getOverflowCls: function() {
        return Ext.baseCSSPrefix + this.layout.direction + '-box-overflow-body';
    },

    initCSSClasses: function() {
        var me = this,
            prefix = Ext.baseCSSPrefix,
            layout = me.layout,
            names = layout.names,
            beforeXName = names.beforeX,
            afterXName = names.afterX,
            type = me.getOwnerType(layout.owner);

        me.beforeCtCls = me.beforeCtCls || prefix + 'box-scroller-' + beforeXName;
        me.afterCtCls  = me.afterCtCls  || prefix + 'box-scroller-' + afterXName;
        
        me.beforeScrollerCls = me.beforeScrollerCls || prefix + type + '-scroll-' + beforeXName;
        me.afterScrollerCls  = me.afterScrollerCls  || prefix + type + '-scroll-' + afterXName;
    },

    beginLayout: function (ownerContext) {
        var layout = this.layout;

        ownerContext.innerCtScrollPos = this.getScrollPosition();

        this.callParent(arguments);
    },

    completeLayout: function(ownerContext) {
        var me = this,
            plan = ownerContext.state.boxPlan,
            names = me.layout.names,
            last;

        
        if (plan && plan.tooNarrow) {
            last = ownerContext.childItems[ownerContext.childItems.length - 1];

            
            me.scrollSize = last.props[names.x] + last.props[names.width];
            me.updateScrollButtons();
        }
        this.callParent(arguments);
    },

    finishedLayout: function(ownerContext) {
        var me = this,
            layout = me.layout,
            scrollPos = Math.min(me.getMaxScrollPosition(), ownerContext.innerCtScrollPos);

        layout.innerCt[layout.names.setScrollLeft](scrollPos);
    },

    handleOverflow: function(ownerContext) {
        var me = this,
            methodName = me.layout.names.getWidth;

        me.showScrollers();
        return {
            reservedSpace: me.beforeCt[methodName]() + me.afterCt[methodName]()
        };
    },

    
    captureChildElements: function() {
        var me = this,
            el = me.layout.owner.el,
            before, after, hoverCls, pressedSuffix, pressedCls, hoverSuffix;

        
        if (!me.beforeCt) {
            hoverSuffix = '-hover';
            pressedSuffix = '-pressed';
            hoverCls = me.scrollerCls + hoverSuffix;
            pressedCls = me.scrollerCls + pressedSuffix;
            before = me.beforeScroller = el.getById(me.layout.owner.id + '-before-scroller');
            after = me.afterScroller = el.getById(me.layout.owner.id + '-after-scroller');
            me.beforeCt = before.up('');
            me.afterCt = after.up('');
            me.createWheelListener();

            before.addClsOnOver(hoverCls);
            before.addClsOnOver(me.beforeScrollerCls + hoverSuffix);
            before.addClsOnClick(pressedCls);
            before.addClsOnClick(me.beforeScrollerCls + pressedSuffix);
            after.addClsOnOver(hoverCls);
            after.addClsOnOver(me.afterScrollerCls + hoverSuffix);
            after.addClsOnClick(pressedCls);
            after.addClsOnClick(me.afterScrollerCls + pressedSuffix);

            before.setVisibilityMode(Ext.Element.DISPLAY);
            after.setVisibilityMode(Ext.Element.DISPLAY);

            me.beforeRepeater = new Ext.util.ClickRepeater(before, {
                interval: me.scrollRepeatInterval,
                handler : me.scrollLeft,
                scope   : me
            });

            me.afterRepeater = new Ext.util.ClickRepeater(after, {
                interval: me.scrollRepeatInterval,
                handler : me.scrollRight,
                scope   : me
            });
        }
    },

    
    createWheelListener: function() {
        var me = this;
        me.layout.innerCt.on({
            mousewheel: function(e) {
                me.scrollBy(me.getWheelDelta(e) * me.wheelIncrement * -1, false);
            },
            stopEvent: true
        });
    },

    getWheelDelta: function (e) {
        return e.getWheelDelta();
    },

    
    clearOverflow: function () {
        this.hideScrollers();
    },

    
    showScrollers: function() {
        var me = this;

        me.captureChildElements();
        me.beforeScroller.show();
        me.afterScroller.show();
        me.layout.owner.addClsWithUI(me.layout.direction === 'vertical' ? 'vertical-scroller' : 'scroller');
        
    },

    
    hideScrollers: function() {
        var me = this;

        if (me.beforeScroller !== undefined) {
            me.beforeScroller.hide();
            me.afterScroller.hide();
            me.layout.owner.removeClsWithUI(me.layout.direction === 'vertical' ? 'vertical-scroller' : 'scroller');
            
        }
    },

    
    destroy: function() {
        var me = this;

        Ext.destroy(me.beforeRepeater, me.afterRepeater, me.beforeScroller, me.afterScroller, me.beforeCt, me.afterCt);
    },

    
    scrollBy: function(delta, animate) {
        this.scrollTo(this.getScrollPosition() + delta, animate);
    },

    
    getScrollAnim: function() {
        return {
            duration: this.scrollDuration, 
            callback: this.updateScrollButtons, 
            scope   : this
        };
    },

    
    updateScrollButtons: function() {
        var me = this,
            beforeMeth,
            afterMeth,
            beforeCls,
            afterCls,
            disabledCls,
            suffix = '-disabled';
            
        if (me.beforeScroller == null || me.afterScroller == null) {
            return;
        }

        beforeMeth = me.atExtremeBefore()  ? 'addCls' : 'removeCls';
        afterMeth  = me.atExtremeAfter() ? 'addCls' : 'removeCls';
        disabledCls = me.scrollerCls + suffix;
        beforeCls = [disabledCls, me.beforeScrollerCls + suffix];
        afterCls = [disabledCls, me.afterScrollerCls  + suffix];

        me.beforeScroller[beforeMeth](beforeCls);
        me.afterScroller[afterMeth](afterCls);
        me.scrolling = false;
    },

    
    scrollLeft: function() {
        this.scrollBy(-this.scrollIncrement, false);
    },

    
    scrollRight: function() {
        this.scrollBy(this.scrollIncrement, false);
    },

    
    getScrollPosition: function(){
        var me = this,
            layout = me.layout,
            result;

        
        
        if (isNaN(me.scrollPosition)) {
            result = layout.innerCt[layout.names.getScrollLeft]();
        } else {
            result = me.scrollPosition;
        }
        return result;
    },

    
    getMaxScrollPosition: function() {
        var me = this,
            layout = me.layout,
            maxScrollPos = me.scrollSize - layout.innerCt[layout.names.getWidth]();

        return (maxScrollPos < 0) ? 0 : maxScrollPos;
    },

    
    atExtremeBefore: function() {
        return !this.getScrollPosition();
    },

    
    atExtremeAfter: function() {
        return this.getScrollPosition() >= this.getMaxScrollPosition();
    },

    
    scrollTo: function(position, animate) {
        var me = this,
            layout = me.layout,
            names = layout.names,
            oldPosition = me.getScrollPosition(),
            newPosition = Ext.Number.constrain(position, 0, me.getMaxScrollPosition());

        if (newPosition != oldPosition && !me.scrolling) {
            me.scrollPosition = NaN;
            if (animate === undefined) {
                animate = me.animateScroll;
            }

            layout.innerCt[names.scrollTo](names.beforeScrollX, newPosition, animate ? me.getScrollAnim() : false);
            if (animate) {
                me.scrolling = true;
            } else {
                me.updateScrollButtons();
            }
            me.fireEvent('scroll', me, newPosition, animate ? me.getScrollAnim() : false);
        }
    },

    
    scrollToItem: function(item, animate) {
        var me = this,
            layout = me.layout,
            owner = layout.owner,
            names = layout.names,
            visibility,
            box,
            newPos;

        item = me.getItem(item);
        if (item !== undefined) {
            if (item == owner.items.first()) {
                newPos = 0
            } else if (item === owner.items.last()) {
                newPos = me.getMaxScrollPosition();
            } else {
                visibility = me.getItemVisibility(item);
                if (!visibility.fullyVisible) {
                    box = item.getBox(false, true);
                    newPos = box[names.x];
                    if (visibility.hiddenEnd) {
                        newPos -= (me.layout.innerCt[names.getWidth]() - box[names.width]);
                    }
                }
            }
            if (newPos !== undefined) {
                me.scrollTo(newPos, animate);
            }
        }
    },

    
    getItemVisibility: function(item) {
        var me          = this,
            box         = me.getItem(item).getBox(true, true),
            layout      = me.layout,
            names       = layout.names,
            itemStart   = box[names.x],
            itemEnd     = itemStart + box[names.width],
            scrollStart = me.getScrollPosition(),
            scrollEnd   = scrollStart + layout.innerCt[names.getWidth]();

        return {
            hiddenStart : itemStart < scrollStart,
            hiddenEnd   : itemEnd > scrollEnd,
            fullyVisible: itemStart > scrollStart && itemEnd < scrollEnd
        };
    }
});



Ext.define('Ext.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) {

        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', {

    

                                  

    statics: {
        
        getRegion: function(el) {
            return Ext.fly(el).getRegion();
        },

        
        from: function(o) {
            return new this(o.top, o.right, o.bottom, o.left);
        }
    },

    

    
    constructor : function(t, r, b, l) {
        var me = this;
        me.y = me.top = me[1] = t;
        me.right = r;
        me.bottom = b;
        me.x = me.left = me[0] = l;
    },

    
    contains : function(region) {
        var me = this;
        return (region.x >= me.x &&
                region.right <= me.right &&
                region.y >= me.y &&
                region.bottom <= me.bottom);

    },

    
    intersect : function(region) {
        var me = this,
            t = Math.max(me.y, region.y),
            r = Math.min(me.right, region.right),
            b = Math.min(me.bottom, region.bottom),
            l = Math.max(me.x, region.x);

        if (b > t && r > l) {
            return new this.self(t, r, b, l);
        }
        else {
            return false;
        }
    },

    
    union : function(region) {
        var me = this,
            t = Math.min(me.y, region.y),
            r = Math.max(me.right, region.right),
            b = Math.max(me.bottom, region.bottom),
            l = Math.min(me.x, region.x);

        return new this.self(t, r, b, l);
    },

    
    constrainTo : function(r) {
        var me = this,
            constrain = Ext.Number.constrain;
        me.top = me.y = constrain(me.top, r.y, r.bottom);
        me.bottom = constrain(me.bottom, r.y, r.bottom);
        me.left = me.x = constrain(me.left, r.x, r.right);
        me.right = constrain(me.right, r.x, r.right);
        return me;
    },

    
    adjust : function(t, r, b, l) {
        var me = this;
        me.top = me.y += t;
        me.left = me.x += l;
        me.right += r;
        me.bottom += b;
        return me;
    },

    
    getOutOfBoundOffset: function(axis, p) {
        if (!Ext.isObject(axis)) {
            if (axis == 'x') {
                return this.getOutOfBoundOffsetX(p);
            } else {
                return this.getOutOfBoundOffsetY(p);
            }
        } else {
            p = axis;
            var d = new Ext.util.Offset();
            d.x = this.getOutOfBoundOffsetX(p.x);
            d.y = this.getOutOfBoundOffsetY(p.y);
            return d;
        }

    },

    
    getOutOfBoundOffsetX: function(p) {
        if (p <= this.x) {
            return this.x - p;
        } else if (p >= this.right) {
            return this.right - p;
        }

        return 0;
    },

    
    getOutOfBoundOffsetY: function(p) {
        if (p <= this.y) {
            return this.y - p;
        } else if (p >= this.bottom) {
            return this.bottom - p;
        }

        return 0;
    },

    
    isOutOfBound: function(axis, p) {
        if (!Ext.isObject(axis)) {
            if (axis == 'x') {
                return this.isOutOfBoundX(p);
            } else {
                return this.isOutOfBoundY(p);
            }
        } else {
            p = axis;
            return (this.isOutOfBoundX(p.x) || this.isOutOfBoundY(p.y));
        }
    },

    
    isOutOfBoundX: function(p) {
        return (p < this.x || p > this.right);
    },

    
    isOutOfBoundY: function(p) {
        return (p < this.y || p > this.bottom);
    },

    
    restrict: function(axis, p, factor) {
        if (Ext.isObject(axis)) {
            var newP;

            factor = p;
            p = axis;

            if (p.copy) {
                newP = p.copy();
            }
            else {
                newP = {
                    x: p.x,
                    y: p.y
                };
            }

            newP.x = this.restrictX(p.x, factor);
            newP.y = this.restrictY(p.y, factor);
            return newP;
        } else {
            if (axis == 'x') {
                return this.restrictX(p, factor);
            } else {
                return this.restrictY(p, factor);
            }
        }
    },

    
    restrictX : function(p, factor) {
        if (!factor) {
            factor = 1;
        }

        if (p <= this.x) {
            p -= (p - this.x) * factor;
        }
        else if (p >= this.right) {
            p -= (p - this.right) * factor;
        }
        return p;
    },

    
    restrictY : function(p, factor) {
        if (!factor) {
            factor = 1;
        }

        if (p <= this.y) {
            p -= (p - this.y) * factor;
        }
        else if (p >= this.bottom) {
            p -= (p - this.bottom) * factor;
        }
        return p;
    },

    
    getSize: function() {
        return {
            width: this.right - this.x,
            height: this.bottom - this.y
        };
    },

    
    copy: function() {
        return new this.self(this.y, this.right, this.bottom, this.x);
    },

    
    copyFrom: function(p) {
        var me = this;
        me.top = me.y = me[1] = p.y;
        me.right = p.right;
        me.bottom = p.bottom;
        me.left = me.x = me[0] = p.x;

        return this;
    },

    
    toString: function() {
        return "Region[" + this.top + "," + this.right + "," + this.bottom + "," + this.left + "]";
    },

    
    translateBy: function(x, y) {
        if (arguments.length == 1) {
            y = x.y;
            x = x.x;
        }
        var me = this;
        me.top = me.y += y;
        me.right += x;
        me.bottom += y;
        me.left = me.x += x;

        return me;
    },

    
    round: function() {
        var me = this;
        me.top = me.y = Math.round(me.y);
        me.right = Math.round(me.right);
        me.bottom = Math.round(me.bottom);
        me.left = me.x = Math.round(me.x);

        return me;
    },

    
    equals: function(region) {
        return (this.top == region.top && this.right == region.right && this.bottom == region.bottom && this.left == region.left);
    }
});





Ext.define('Ext.dd.DragDropManager', {
    singleton: true,

                                  

                                      

    
    alternateClassName: ['Ext.dd.DragDropMgr', 'Ext.dd.DDM'],

    
    ids: {},

    
    handleIds: {},

    
    dragCurrent: null,

    
    dragOvers: {},

    
    deltaX: 0,

    
    deltaY: 0,

    
    preventDefault: true,

    
    stopPropagation: true,

    
    initialized: false,

    
    locked: false,

    
    init: function() {
        this.initialized = true;
    },

    
    POINT: 0,

    
    INTERSECT: 1,

    
    mode: 0,

    
    notifyOccluded: false,

    
    dragCls: Ext.baseCSSPrefix + 'dd-drag-current',

    
    _execOnAll: function(sMethod, args) {
        var ids = this.ids,
            i, j, oDD, item;
            
        for (i in ids) {
            if (ids.hasOwnProperty(i)) {
                item = ids[i];
                for (j in item) {
                    if (item.hasOwnProperty(j)) {
                        oDD = item[j];
                        if (! this.isTypeOfDD(oDD)) {
                            continue;
                        }
                        oDD[sMethod].apply(oDD, args);
                    }
                }
            }
        }
    },

    
    _onLoad: function() {

        this.init();

        var Event = Ext.EventManager;
        Event.on(document, "mouseup",   this.handleMouseUp, this, true);
        Event.on(document, "mousemove", this.handleMouseMove, this, true);
        Event.on(window,   "unload",    this._onUnload, this, true);
        Event.on(window,   "resize",    this._onResize, this, true);
        

    },

    
    _onResize: function(e) {
        this._execOnAll("resetConstraints", []);
    },

    
    lock: function() { this.locked = true; },

    
    unlock: function() { this.locked = false; },

    
    isLocked: function() { return this.locked; },

    
    locationCache: {},

    
    useCache: true,

    
    clickPixelThresh: 3,

    
    clickTimeThresh: 350,

    
    dragThreshMet: false,

    
    clickTimeout: null,

    
    startX: 0,

    
    startY: 0,

    
    regDragDrop: function(oDD, sGroup) {
        if (!this.initialized) { this.init(); }

        if (!this.ids[sGroup]) {
            this.ids[sGroup] = {};
        }
        this.ids[sGroup][oDD.id] = oDD;
    },

    
    removeDDFromGroup: function(oDD, sGroup) {
        if (!this.ids[sGroup]) {
            this.ids[sGroup] = {};
        }

        var obj = this.ids[sGroup];
        if (obj && obj[oDD.id]) {
            delete obj[oDD.id];
        }
    },

    
    _remove: function(oDD, clearGroup) {
        
        
        if (this.clearingAll) {
            return;
        }
        
        var ids = this.ids,
            groups = oDD.groups,
            g;
            
        for (g in groups) {
            if (groups.hasOwnProperty(g)) {
                if (clearGroup) {
                    delete ids[g];
                } else if (ids[g]) {
                    delete ids[g][oDD.id];
                }
            }
        }
        delete this.handleIds[oDD.id];
    },

    
    regHandle: function(sDDId, sHandleId) {
        if (!this.handleIds[sDDId]) {
            this.handleIds[sDDId] = {};
        }
        this.handleIds[sDDId][sHandleId] = sHandleId;
    },

    
    isDragDrop: function(id) {
        return ( this.getDDById(id) ) ? true : false;
    },

    
    getRelated: function(p_oDD, bTargetsOnly) {
        var oDDs = [],
            i, j, dd;
        for (i in p_oDD.groups) {
            for (j in this.ids[i]) {
                dd = this.ids[i][j];
                if (! this.isTypeOfDD(dd)) {
                    continue;
                }
                if (!bTargetsOnly || dd.isTarget) {
                    oDDs[oDDs.length] = dd;
                }
            }
        }

        return oDDs;
    },

    
    isLegalTarget: function (oDD, oTargetDD) {
        var targets = this.getRelated(oDD, true),
            i, len;
        for (i=0, len=targets.length;i<len;++i) {
            if (targets[i].id == oTargetDD.id) {
                return true;
            }
        }

        return false;
    },

    
    isTypeOfDD: function (oDD) {
        return (oDD && oDD.__ygDragDrop);
    },

    
    isHandle: function(sDDId, sHandleId) {
        return ( this.handleIds[sDDId] &&
                        this.handleIds[sDDId][sHandleId] );
    },

    
    getDDById: function(id) {
        var i, dd;
        for (i in this.ids) {
            dd = this.ids[i][id];
            if (dd instanceof Ext.dd.DDTarget) {
                return dd;
            }
        }
        return null;
    },

    
    handleMouseDown: function(e, oDD) {
        var me = this,
            el;

        if (Ext.quickTipsActive){
            Ext.tip.QuickTipManager.ddDisable();
        }
        if (me.dragCurrent){
            
            
            me.handleMouseUp(e);
        }

        me.mousedownEvent = e.clone();
        me.currentTarget = e.getTarget();
        me.dragCurrent = oDD;

        el = oDD.getEl();

        
        
        
        
        if (Ext.isIE9m && el.setCapture) {
            el.setCapture();
        }

        
        me.startX = e.getPageX();
        me.startY = e.getPageY();

        me.deltaX = me.startX - el.offsetLeft;
        me.deltaY = me.startY - el.offsetTop;

        me.dragThreshMet = false;

        me.clickTimeout = setTimeout(
            function() {
                me.startDrag(me.startX, me.startY);
            },
            me.clickTimeThresh
        );
    },

    
    startDrag: function(x, y) {
        var me = this,
            current = me.dragCurrent,
            dragEl;

        clearTimeout(me.clickTimeout);
        if (current) {
            current.b4StartDrag(x, y);
            current.startDrag(x, y);
            dragEl = current.getDragEl();

            
            if (dragEl) {
                Ext.fly(dragEl).addCls(me.dragCls);
            }
        }
        me.dragThreshMet = true;
    },

    
    handleMouseUp: function(e) {
        var me = this;

        if (Ext.quickTipsActive){
            Ext.tip.QuickTipManager.ddEnable();
        }
        if (!me.dragCurrent) {
            return;
        }

        
        if (Ext.isIE && document.releaseCapture) {
            document.releaseCapture();
        }

        clearTimeout(me.clickTimeout);

        if (me.dragThreshMet) {
            me.fireEvents(e, true);
        }

        me.stopDrag(e);

        me.stopEvent(e);
    },

    
    stopEvent: function(e) {
        if (this.stopPropagation) {
            e.stopPropagation();
        }

        if (this.preventDefault) {
            e.preventDefault();
        }
    },

    
    stopDrag: function(e) {
        var me = this,
            current = me.dragCurrent,
            dragEl;

        
        if (current) {
            if (me.dragThreshMet) {

                
                dragEl = current.getDragEl();
                if (dragEl) {
                    Ext.fly(dragEl).removeCls(me.dragCls);
                }

                current.b4EndDrag(e);
                current.endDrag(e);
            }

            me.dragCurrent.onMouseUp(e);
        }

        me.dragCurrent = null;
        me.dragOvers = {};
    },

    
    handleMouseMove: function(e) {
        var me = this,
            current = me.dragCurrent,
            diffX,
            diffY;

        if (!current) {
            return true;
        }

        if (!me.dragThreshMet) {
            diffX = Math.abs(me.startX - e.getPageX());
            diffY = Math.abs(me.startY - e.getPageY());
            if (diffX > me.clickPixelThresh || diffY > me.clickPixelThresh) {
                me.startDrag(me.startX, me.startY);
            }
        }

        if (me.dragThreshMet) {
            current.b4Drag(e);
            current.onDrag(e);
            if (!current.moveOnly) {
                me.fireEvents(e, false);
            }
        }

        me.stopEvent(e);

        return true;
    },

    
    fireEvents: function(e, isDrop) {
        var me = this,
            dragCurrent = me.dragCurrent,
            dragEl,
            oldDragElTop,
            mousePoint = e.getPoint(),
            overTarget,
            overTargetEl,
            allTargets = [],
            oldOvers  = [],  
            outEvts   = [],
            overEvts  = [],
            dropEvts  = [],
            enterEvts = [],
            xy,
            needsSort,
            i,
            len,
            sGroup;

        
        
        if (!dragCurrent || dragCurrent.isLocked()) {
            return;
        }

        
        
        
        
        
        
        if (!me.notifyOccluded && (!Ext.supports.PointerEvents || Ext.isIE10m || Ext.isOpera) && !(dragCurrent.deltaX < 0 || dragCurrent.deltaY < 0)) {
            dragEl = dragCurrent.getDragEl();
            oldDragElTop = dragEl.style.top;
            dragEl.style.top = '-10000px';
            xy = e.getXY();
            e.target = document.elementFromPoint(xy[0], xy[1]);
            dragEl.style.top = oldDragElTop;
        }

        
        
        for (i in me.dragOvers) {

            overTarget = me.dragOvers[i];

            if (!me.isTypeOfDD(overTarget)) {
                continue;
            }

            
            if (me.notifyOccluded) {
                if (!this.isOverTarget(mousePoint, overTarget, me.mode)) {
                    outEvts.push(overTarget);
                }
            }
            
            else {
                if (!e.within(overTarget.getEl())) {
                    outEvts.push(overTarget);
                }
            }

            oldOvers[i] = true;
            delete me.dragOvers[i];
        }

        
        
        
        for (sGroup in dragCurrent.groups) {

            if ("string" != typeof sGroup) {
                continue;
            }

            
            for (i in me.ids[sGroup]) {
                overTarget = me.ids[sGroup][i];

                
                
                
                
                
                
                if (me.isTypeOfDD(overTarget) &&
                    (overTargetEl = overTarget.getEl()) &&
                    (overTarget.isTarget) &&
                    (!overTarget.isLocked()) &&
                    (Ext.fly(overTargetEl).isVisible(true)) &&
                    ((overTarget != dragCurrent) || (dragCurrent.ignoreSelf === false))) {

                    
                    if (me.notifyOccluded) {

                        
                        if ((overTarget.zIndex = me.getZIndex(overTargetEl)) !== -1) {
                            needsSort = true;
                        }
                        allTargets.push(overTarget);
                    }
                    
                    else {
                        if (e.within(overTarget.getEl())) {
                            allTargets.push(overTarget);
                            break;
                        }
                    }
                }
            }
        }

        
        if (needsSort) {
            Ext.Array.sort(allTargets, me.byZIndex);
        }

        
        
        for (i = 0, len = allTargets.length; i < len; i++) {
            overTarget = allTargets[i];

            
            if (me.isOverTarget(mousePoint, overTarget, me.mode)) {
                
                if (isDrop) {
                    dropEvts.push( overTarget );
                
                } else {

                    
                    if (!oldOvers[overTarget.id]) {
                        enterEvts.push( overTarget );
                    
                    } else {
                        overEvts.push( overTarget );
                    }
                    me.dragOvers[overTarget.id] = overTarget;
                }

                
                if (!me.notifyOccluded) {
                    break;
                }
            }
        }

        if (me.mode) {
            if (outEvts.length) {
                dragCurrent.b4DragOut(e, outEvts);
                dragCurrent.onDragOut(e, outEvts);
            }

            if (enterEvts.length) {
                dragCurrent.onDragEnter(e, enterEvts);
            }

            if (overEvts.length) {
                dragCurrent.b4DragOver(e, overEvts);
                dragCurrent.onDragOver(e, overEvts);
            }

            if (dropEvts.length) {
                dragCurrent.b4DragDrop(e, dropEvts);
                dragCurrent.onDragDrop(e, dropEvts);
            }

        } else {
            
            for (i=0, len=outEvts.length; i<len; ++i) {
                dragCurrent.b4DragOut(e, outEvts[i].id);
                dragCurrent.onDragOut(e, outEvts[i].id);
            }

            
            for (i=0,len=enterEvts.length; i<len; ++i) {
                
                dragCurrent.onDragEnter(e, enterEvts[i].id);
            }

            
            for (i=0,len=overEvts.length; i<len; ++i) {
                dragCurrent.b4DragOver(e, overEvts[i].id);
                dragCurrent.onDragOver(e, overEvts[i].id);
            }

            
            for (i=0, len=dropEvts.length; i<len; ++i) {
                dragCurrent.b4DragDrop(e, dropEvts[i].id);
                dragCurrent.onDragDrop(e, dropEvts[i].id);
            }

        }

        
        if (isDrop && !dropEvts.length) {
            dragCurrent.onInvalidDrop(e);
        }

    },

    
    getZIndex: function(element) {
        var body = document.body,
            z,
            zIndex = -1;

        element = Ext.getDom(element);
        while (element !== body) {
            if (!isNaN(z = Number(Ext.fly(element).getStyle('zIndex')))) {
                zIndex = z;
            }
            element = element.parentNode;
        }
        return zIndex;
    },

    
    byZIndex: function(d1, d2) {
        return d1.zIndex < d2.zIndex;
    },

    
    getBestMatch: function(dds) {
        var winner = null,
            len = dds.length,
            i, dd;
        
        
           
        
        


        if (len == 1) {
            winner = dds[0];
        } else {
            
            for (i=0; i<len; ++i) {
                dd = dds[i];
                
                
                
                if (dd.cursorIsOver) {
                    winner = dd;
                    break;
                
                } else {
                    if (!winner ||
                        winner.overlap.getArea() < dd.overlap.getArea()) {
                        winner = dd;
                    }
                }
            }
        }

        return winner;
    },

    
    refreshCache: function(groups) {
        var sGroup, i, oDD, loc;
        for (sGroup in groups) {
            if ("string" != typeof sGroup) {
                continue;
            }
            for (i in this.ids[sGroup]) {
                oDD = this.ids[sGroup][i];

                if (this.isTypeOfDD(oDD)) {
                
                    loc = this.getLocation(oDD);
                    if (loc) {
                        this.locationCache[oDD.id] = loc;
                    } else {
                        delete this.locationCache[oDD.id];
                        
                        
                        
                    }
                }
            }
        }
    },

    
    verifyEl: function(el) {
        if (el) {
            var parent;
            if(Ext.isIE){
                try{
                    parent = el.offsetParent;
                }catch(e){}
            }else{
                parent = el.offsetParent;
            }
            if (parent) {
                return true;
            }
        }

        return false;
    },

    
    getLocation: function(oDD) {
        if (! this.isTypeOfDD(oDD)) {
            return null;
        }

        
        
        if (oDD.getRegion) {
            return oDD.getRegion();
        }

        var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;

        try {
            pos= Ext.Element.getXY(el);
        } catch (e) { }

        if (!pos) {
            return null;
        }

        x1 = pos[0];
        x2 = x1 + el.offsetWidth;
        y1 = pos[1];
        y2 = y1 + el.offsetHeight;

        t = y1 - oDD.padding[0];
        r = x2 + oDD.padding[1];
        b = y2 + oDD.padding[2];
        l = x1 - oDD.padding[3];

        return new Ext.util.Region(t, r, b, l);
    },

    
    isOverTarget: function(pt, oTarget, intersect) {
        
        var loc = this.locationCache[oTarget.id],
            dc,
            pos,
            el,
            curRegion,
            overlap;

        if (!loc || !this.useCache) {
            loc = this.getLocation(oTarget);
            this.locationCache[oTarget.id] = loc;
        }

        if (!loc) {
            return false;
        }

        oTarget.cursorIsOver = loc.contains( pt );

        
        
        
        
        
        dc = this.dragCurrent;
        if (!dc || !dc.getTargetCoord ||
                (!intersect && !dc.constrainX && !dc.constrainY)) {
            return oTarget.cursorIsOver;
        }

        oTarget.overlap = null;

        
        
        
        
        pos = dc.getTargetCoord(pt.x, pt.y);

        el = dc.getDragEl();
        curRegion = new Ext.util.Region(pos.y,
            pos.x + el.offsetWidth,
            pos.y + el.offsetHeight,
            pos.x
        );

        overlap = curRegion.intersect(loc);

        if (overlap) {
            oTarget.overlap = overlap;
            return (intersect) ? true : oTarget.cursorIsOver;
        } else {
            return false;
        }
    },

    
    _onUnload: function(e, me) {
        Ext.dd.DragDropManager.unregAll();
    },

    
    unregAll: function() {
        var me = this,
            cache = me.elementCache,
            i;
            
        if (me.dragCurrent) {
            me.stopDrag();
            me.dragCurrent = null;
        }

        me.clearingAll = true;
        me._execOnAll("unreg", []);
        delete me.clearingAll;

        for (i in cache) {
            delete cache[i];
        }

        me.elementCache = {};
        me.ids = {};
        me.handleIds = {};
    },

    
    elementCache: {},

    
    getElWrapper: function(id) {
        var oWrapper = this.elementCache[id];
        if (!oWrapper || !oWrapper.el) {
            oWrapper = this.elementCache[id] =
                new this.ElementWrapper(Ext.getDom(id));
        }
        return oWrapper;
    },

    
    getElement: function(id) {
        return Ext.getDom(id);
    },

    
    getCss: function(id) {
        var el = Ext.getDom(id);
        return (el) ? el.style : null;
    },

    
    ElementWrapper: function(el) {
        
        this.el = el || null;
        
        this.id = this.el && el.id;
        
        this.css = this.el && el.style;
    },

    
    

    
    getPosX: function(el) {
        return Ext.Element.getX(el);
    },

    
    getPosY: function(el) {
        return Ext.Element.getY(el);
    },

    
    swapNode: function(n1, n2) {
        if (n1.swapNode) {
            n1.swapNode(n2);
        } else {
            var p = n2.parentNode,
                s = n2.nextSibling;

            if (s == n1) {
                p.insertBefore(n1, n2);
            } else if (n2 == n1.nextSibling) {
                p.insertBefore(n2, n1);
            } else {
                n1.parentNode.replaceChild(n2, n1);
                p.insertBefore(n1, s);
            }
        }
    },

    
    getScroll: function () {
        var doc   = window.document,
            docEl = doc.documentElement,
            body  = doc.body,
            top   = 0,
            left  = 0;

        if (Ext.isGecko4) {
            top  = window.scrollYOffset;
            left = window.scrollXOffset;
        } else {
            if (docEl && (docEl.scrollTop || docEl.scrollLeft)) {
                top  = docEl.scrollTop;
                left = docEl.scrollLeft;
            } else if (body) {
                top  = body.scrollTop;
                left = body.scrollLeft;
            }
        }
        return {
            top: top,
            left: left
        };
    },

    
    getStyle: function(el, styleProp) {
        return Ext.fly(el).getStyle(styleProp);
    },

    
    getScrollTop: function () {
        return this.getScroll().top;
    },

    
    getScrollLeft: function () {
        return this.getScroll().left;
    },

    
    moveToEl: function (moveEl, targetEl) {
        var aCoord = Ext.Element.getXY(targetEl);
        Ext.Element.setXY(moveEl, aCoord);
    },

    
    numericSort: function(a, b) {
        return (a - b);
    },

    
    _timeoutCount: 0,

    
    _addListeners: function() {
        if ( document ) {
            this._onLoad();
        } else {
            if (this._timeoutCount <= 2000) {
                setTimeout(this._addListeners, 10);
                if (document && document.body) {
                    this._timeoutCount += 1;
                }
            }
        }
    },

    
    handleWasClicked: function(node, id) {
        if (this.isHandle(id, node.id)) {
            return true;
        } else {
            
            var p = node.parentNode;

            while (p) {
                if (this.isHandle(id, p.id)) {
                    return true;
                } else {
                    p = p.parentNode;
                }
            }
        }

        return false;
    }
}, function() {
    this._addListeners();
});


Ext.define('Ext.layout.container.Box', {

    

    alias: ['layout.box'],
    extend:  Ext.layout.container.Container ,
    alternateClassName: 'Ext.layout.BoxLayout',

               
                                                
                                                
                                                    
                          
                                
      

    

    
    defaultMargins: {
        top: 0,
        right: 0,
        bottom: 0,
        left: 0
    },

    
    padding: 0,

    
    pack: 'start',

    
    flex: undefined,

    
    stretchMaxPartner: undefined,

    alignRoundingMethod: 'round',

    type: 'box',
    scrollOffset: 0,
    itemCls: Ext.baseCSSPrefix + 'box-item',
    targetCls: Ext.baseCSSPrefix + 'box-layout-ct',
    targetElCls: Ext.baseCSSPrefix + 'box-target',
    innerCls: Ext.baseCSSPrefix + 'box-inner',

    
    
    availableSpaceOffset: 0,

    
    reserveOffset: true,

    manageMargins: true,
    
    createsInnerCt: true,

    childEls: [
        'innerCt',
        'targetEl'
    ],

    renderTpl: [
        '{%var oc,l=values.$comp.layout,oh=l.overflowHandler;',
        'if (oh.getPrefixConfig!==Ext.emptyFn) {',
            'if(oc=oh.getPrefixConfig())dh.generateMarkup(oc, out)',
        '}%}',
        '<div id="{ownerId}-innerCt" class="{[l.innerCls]} {[oh.getOverflowCls()]}" role="presentation">',
            '<div id="{ownerId}-targetEl" class="{targetElCls}" role="presentation">',
                '{%this.renderBody(out, values)%}',
            '</div>',
        '</div>',
        '{%if (oh.getSuffixConfig!==Ext.emptyFn) {',
            'if(oc=oh.getSuffixConfig())dh.generateMarkup(oc, out)',
        '}%}',
        {
            disableFormats: true,
            definitions: 'var dh=Ext.DomHelper;'
        }
    ],

    constructor: function(config) {
        var me = this,
            type;

        me.callParent(arguments);

        
        me.flexSortFn = Ext.Function.bind(me.flexSort, me);

        me.initOverflowHandler();

        type = typeof me.padding;
        if (type == 'string' || type == 'number') {
            me.padding = Ext.util.Format.parseBox(me.padding);
            me.padding.height = me.padding.top  + me.padding.bottom;
            me.padding.width  = me.padding.left + me.padding.right;
        }
    },

    
    
    _percentageRe: /^\s*(\d+(?:\.\d*)?)\s*[%]\s*$/,

    getItemSizePolicy: function (item, ownerSizeModel) {
        var me = this,
            policy = me.sizePolicy,
            align = me.align,
            flex = item.flex,
            key = align,
            names = me.names,
            width = item[names.width],
            height = item[names.height],
            percentageRe = me._percentageRe,
            percentageWidth = percentageRe.test(width),
            isStretch = (align == 'stretch'),
            isStretchMax = (align == 'stretchmax'),
            constrain = me.constrainAlign;
            
        
        if (!ownerSizeModel && (isStretch || flex || percentageWidth || (constrain && !isStretchMax))) {
            ownerSizeModel = me.owner.getSizeModel();
        }

        if (isStretch) {
            
            
            if (!percentageRe.test(height) && ownerSizeModel[names.height].shrinkWrap) {
                key = 'stretchmax';
                
                
                
            }
        } else if (!isStretchMax) {
            if (percentageRe.test(height)) {
                
                
                key = 'stretch';
            } else if (constrain && !ownerSizeModel[names.height].shrinkWrap) {
                
                
                key = 'stretchmax';
            } else {
                key = '';
            }
        }

        if (flex || percentageWidth) {
            
            
            if (!ownerSizeModel[names.width].shrinkWrap) {
                policy = policy.flex; 
            }
        }

        return policy[key];
    },

    flexSort: function (a, b) {
        
        
        
        
        
        var maxWidthName = this.names.maxWidth,
            minWidthName = this.names.minWidth,
            infiniteValue = Infinity,
            aTarget = a.target,
            bTarget = b.target,
            result = 0,
            aMin, bMin, aMax, bMax,
            hasMin, hasMax;

        aMax = aTarget[maxWidthName] || infiniteValue;
        bMax = bTarget[maxWidthName] || infiniteValue;
        aMin = aTarget[minWidthName] || 0;
        bMin = bTarget[minWidthName] || 0;
        
        hasMin = isFinite(aMin) || isFinite(bMin);
        hasMax = isFinite(aMax) || isFinite(bMax);

        if (hasMin || hasMax) {
            if (hasMax) {
                result = aMax - bMax;
            }
            
            
            
            
            if (result === 0 && hasMin) {
                result = bMin - aMin;
            }
        }
        return result;
    },

    isItemBoxParent: function (itemContext) {
        return true;
    },

    isItemShrinkWrap: function (item) {
        return true;
    },

    roundFlex: function(width) {
        return Math.ceil(width);
    },

    
    beginCollapse: function(child) {
        var me = this;

        if (me.direction === 'vertical' && child.collapsedVertical()) {
            child.collapseMemento.capture(['flex']);
            delete child.flex;
        } else if (me.direction === 'horizontal' && child.collapsedHorizontal()) {
            child.collapseMemento.capture(['flex']);
            delete child.flex;
        }
    },

    
    beginExpand: function(child) {

        
        child.collapseMemento.restore(['flex']);
    },

    beginLayout: function (ownerContext) {
        var me = this,
            owner = me.owner,
            smp = owner.stretchMaxPartner,
            style = me.innerCt.dom.style,
            names = me.names;

        ownerContext.boxNames = names;

        
        
        me.overflowHandler.beginLayout(ownerContext);

        
        if (typeof smp === 'string') {
            smp = Ext.getCmp(smp) || owner.query(smp)[0];
        }

        ownerContext.stretchMaxPartner = smp && ownerContext.context.getCmp(smp);

        me.callParent(arguments);

        ownerContext.innerCtContext = ownerContext.getEl('innerCt', me);

        
        me.scrollParallel = owner.scrollFlags[names.x];

        
        me.scrollPerpendicular = owner.scrollFlags[names.y];

        
        if (me.scrollParallel) {
            me.scrollPos = owner.getTargetEl().dom[names.scrollLeft];
        }

        
        style.width = '';
        style.height = '';
    },

    beginLayoutCycle: function (ownerContext, firstCycle) {
        var me = this,
            align = me.align,
            names = ownerContext.boxNames,
            pack = me.pack,
            heightModelName = names.heightModel;

        
        
        me.overflowHandler.beginLayoutCycle(ownerContext, firstCycle);

        me.callParent(arguments);

        
        

        ownerContext.parallelSizeModel      = ownerContext[names.widthModel];
        ownerContext.perpendicularSizeModel = ownerContext[heightModelName];

        ownerContext.boxOptions = {
            align: align = {
                stretch:    align == 'stretch',
                stretchmax: align == 'stretchmax',
                center:     align == names.center,
                bottom:     align == names.afterY
            },
            pack: pack = {
                center: pack == 'center',
                end:    pack == 'end'
            }
        };

        
        
        
        
        

        if (align.stretch && ownerContext.perpendicularSizeModel.shrinkWrap) {
            align.stretchmax = true;
            align.stretch = false;
        }

        
        align.nostretch = !(align.stretch || align.stretchmax);

        
        
        

        if (ownerContext.parallelSizeModel.shrinkWrap) {
            pack.center = pack.end = false;
        }

        me.cacheFlexes(ownerContext);

        
        
        
        
        
        
        me.targetEl.setWidth(20000);
    },

    
    cacheFlexes: function (ownerContext) {
        var me = this,
            names = ownerContext.boxNames,
            widthModelName = names.widthModel,
            heightModelName = names.heightModel,
            nostretch = ownerContext.boxOptions.align.nostretch,
            totalFlex = 0,
            childItems = ownerContext.childItems,
            i = childItems.length,
            flexedItems = [],
            minWidth = 0,
            minWidthName = names.minWidth,
            percentageRe = me._percentageRe,
            percentageWidths = 0,
            percentageHeights = 0,
            child, childContext, flex, match;

        while (i--) {
            childContext = childItems[i];
            child = childContext.target;

            
            
            
            if (childContext[widthModelName].calculated) {
                childContext.flex = flex = child.flex;
                if (flex) {
                    totalFlex += flex;
                    flexedItems.push(childContext);
                    minWidth += child[minWidthName] || 0;
                } else { 
                    match = percentageRe.exec(child[names.width]);
                    childContext.percentageParallel = parseFloat(match[1]) / 100;
                    ++percentageWidths;
                }
            }
            
            

            if (nostretch && childContext[heightModelName].calculated) {
                
                
                match = percentageRe.exec(child[names.height]);
                childContext.percentagePerpendicular = parseFloat(match[1]) / 100;
                ++percentageHeights;
            }
        }

        ownerContext.flexedItems = flexedItems;
        ownerContext.flexedMinSize = minWidth;
        ownerContext.totalFlex = totalFlex;
        ownerContext.percentageWidths = percentageWidths;
        ownerContext.percentageHeights = percentageHeights;

        
        
        
        Ext.Array.sort(flexedItems, me.flexSortFn);
    },

    calculate: function(ownerContext) {
        var me = this,
            targetSize = me.getContainerSize(ownerContext),
            names = ownerContext.boxNames,
            state = ownerContext.state,
            plan = state.boxPlan || (state.boxPlan = {}),
            targetContext = ownerContext.targetContext;

        plan.targetSize = targetSize;

        if (!state.parallelDone) {
            state.parallelDone = me.calculateParallel(ownerContext, names, plan);
        }

        if (!state.perpendicularDone) {
            state.perpendicularDone = me.calculatePerpendicular(ownerContext, names, plan);
        }

        if (state.parallelDone && state.perpendicularDone) {
            
            
            
            
            if (me.owner.dock && (Ext.isIE7m || Ext.isIEQuirks) && !me.owner.width && !me.horizontal) {
                plan.isIEVerticalDock = true;
                plan.calculatedWidth = plan.maxSize + ownerContext.getPaddingInfo().width + ownerContext.getFrameInfo().width;
                if (targetContext !== ownerContext) {
                    
                    
                    
                    plan.calculatedWidth += targetContext.getPaddingInfo().width;
                }
            }

            me.publishInnerCtSize(ownerContext, me.reserveOffset ? me.availableSpaceOffset : 0);

            
            
            if (me.done && ownerContext.boxOptions.align.stretchmax && !state.stretchMaxDone) {
                me.calculateStretchMax(ownerContext, names, plan);
                state.stretchMaxDone = true;
            }
            me.overflowHandler.calculate(ownerContext);
        } else {
            me.done = false;
        }
    },

    calculateParallel: function(ownerContext, names, plan) {
        var me = this,
            widthName = names.width,
            childItems = ownerContext.childItems,
            beforeXName = names.beforeX,
            afterXName = names.afterX,
            setWidthName = names.setWidth,
            childItemsLength = childItems.length,
            flexedItems = ownerContext.flexedItems,
            flexedItemsLength = flexedItems.length,
            pack = ownerContext.boxOptions.pack,
            padding = me.padding,
            targetSize = plan.targetSize,
            containerWidth = targetSize[widthName],
            totalMargin = 0,
            left = padding[beforeXName],
            nonFlexWidth = left + padding[afterXName] + me.scrollOffset +
                                    (me.reserveOffset ? me.availableSpaceOffset : 0),
            scrollbarWidth = Ext.getScrollbarSize()[names.width],
            i, childMargins, remainingWidth, remainingFlex, childContext, flex, flexedWidth,
            contentWidth, mayNeedScrollbarAdjust, childWidth, percentageSpace;
            
        
        
        
        
        if (!ownerContext.parallelSizeModel.shrinkWrap && !targetSize[names.gotWidth]) {
            return false;
        }

        
        
        
        
        
        
        if (scrollbarWidth &&
            me.scrollPerpendicular &&
            ownerContext.parallelSizeModel.shrinkWrap &&
            !ownerContext.boxOptions.align.stretch &&
            !ownerContext.perpendicularSizeModel.shrinkWrap) {

            
            
            
            if (!ownerContext.state.perpendicularDone) {
                return false;
            }
            mayNeedScrollbarAdjust = true;
        }

        
        for (i = 0; i < childItemsLength; ++i) {
            childContext = childItems[i];
            childMargins = childContext.marginInfo || childContext.getMarginInfo();

            totalMargin += childMargins[widthName];

            if (!childContext[names.widthModel].calculated) {
                childWidth = childContext.getProp(widthName);
                nonFlexWidth += childWidth; 
                if (isNaN(nonFlexWidth)) {
                    return false;
                }
            }
        }

        nonFlexWidth += totalMargin;
        if (ownerContext.percentageWidths) {
            percentageSpace = containerWidth - totalMargin;
            if (isNaN(percentageSpace)) {
                return false;
            }

            for (i = 0; i < childItemsLength; ++i) {
                childContext = childItems[i];
                if (childContext.percentageParallel) {
                    childWidth = Math.ceil(percentageSpace * childContext.percentageParallel);
                    childWidth = childContext.setWidth(childWidth);
                    nonFlexWidth += childWidth;
                }
            }
        }

        

        if (ownerContext.parallelSizeModel.shrinkWrap) {
            plan.availableSpace = 0;
            plan.tooNarrow = false;
        } else {
            plan.availableSpace = containerWidth - nonFlexWidth;

            
            plan.tooNarrow = plan.availableSpace < ownerContext.flexedMinSize;
            if (plan.tooNarrow && Ext.getScrollbarSize()[names.height] && me.scrollParallel && ownerContext.state.perpendicularDone) {
                ownerContext.state.perpendicularDone = false;
                for (i = 0; i < childItemsLength; ++i) {
                    childItems[i].invalidate();
                }
            }
        }

        contentWidth = nonFlexWidth;
        remainingWidth = plan.availableSpace;
        remainingFlex = ownerContext.totalFlex;

        
        for (i = 0; i < flexedItemsLength; i++) {
            childContext = flexedItems[i];
            flex         = childContext.flex;
            flexedWidth  = me.roundFlex((flex / remainingFlex) * remainingWidth);
            flexedWidth  = childContext[setWidthName](flexedWidth); 

            

            contentWidth   += flexedWidth;
            
            remainingWidth  = Math.max(0, remainingWidth - flexedWidth); 
            remainingFlex  -= flex;
        }

        if (pack.center) {
            left += remainingWidth / 2;

            
            if (left < 0) {
                left = 0;
            }
        } else if (pack.end) {
            left += remainingWidth;
        }

        
        for (i = 0; i < childItemsLength; ++i) {
            childContext = childItems[i];
            childMargins = childContext.marginInfo; 

            left += childMargins[beforeXName];

            childContext.setProp(names.x, left);

            
            
            
            
            left += childMargins[afterXName] + childContext.props[widthName];
        }

        contentWidth += ownerContext.targetContext.getPaddingInfo()[widthName];

        
        ownerContext.state.contentWidth = contentWidth; 

        
        
        if (mayNeedScrollbarAdjust &&
            (ownerContext.peek(names.contentHeight) > plan.targetSize[names.height])) {
            contentWidth += scrollbarWidth;
            ownerContext[names.hasOverflowY] = true;

            
            ownerContext.target.componentLayout[names.setWidthInDom] = true;

            
            
            
            
            ownerContext[names.invalidateScrollY] = Ext.isStrict && Ext.isIE8;
        }
        ownerContext[names.setContentWidth](contentWidth);

        return true;
    },

    calculatePerpendicular: function(ownerContext, names, plan) {
        var me = this,
            heightShrinkWrap = ownerContext.perpendicularSizeModel.shrinkWrap,
            targetSize = plan.targetSize,
            childItems = ownerContext.childItems,
            childItemsLength = childItems.length,
            mmax = Math.max,
            heightName = names.height,
            setHeightName = names.setHeight,
            beforeYName = names.beforeY,
            topPositionName = names.y,
            padding = me.padding,
            top = padding[beforeYName],
            availHeight = targetSize[heightName] - top - padding[names.afterY],
            align = ownerContext.boxOptions.align,
            isStretch    = align.stretch, 
            isStretchMax = align.stretchmax,
            isCenter     = align.center,
            isBottom     = align.bottom,
            constrain    = me.constrainAlign,
            maxHeight = 0,
            hasPercentageSizes = 0,
            onBeforeInvalidateChild = me.onBeforeConstrainInvalidateChild,
            onAfterInvalidateChild = me.onAfterConstrainInvalidateChild,
            scrollbarHeight = Ext.getScrollbarSize().height,
            childTop, i, childHeight, childMargins, diff, height, childContext,
            stretchMaxPartner, stretchMaxChildren, shrinkWrapParallelOverflow, 
            percentagePerpendicular;

        if (isStretch || ((isCenter || isBottom) && !heightShrinkWrap)) {
            if (isNaN(availHeight)) {
                return false;
            }
        }
        
        
        
        
        
        if (!isStretch && !ownerContext.parallelSizeModel.shrinkWrap && !ownerContext.state.parallelDone && me.scrollParallel) {
            return false;
        }

        
        
        
        
        
        
        if (me.scrollParallel && plan.tooNarrow) {
            if (heightShrinkWrap) {
                shrinkWrapParallelOverflow = true;
            } else {
                availHeight -= scrollbarHeight;
                plan.targetSize[heightName] -= scrollbarHeight;
            }
        }

        if (isStretch) {
            height = availHeight; 
        } else {
            for (i = 0; i < childItemsLength; i++) {
                childContext = childItems[i];
                childMargins = (childContext.marginInfo || childContext.getMarginInfo())[heightName];

                if (!(percentagePerpendicular = childContext.percentagePerpendicular)) {
                    childHeight = childContext.getProp(heightName);
                } else {
                    ++hasPercentageSizes;
                    if (heightShrinkWrap) {
                        
                        
                        continue;
                    } else {
                        childHeight = percentagePerpendicular * availHeight - childMargins;
                        childHeight = childContext[names.setHeight](childHeight);
                    }
                }
                
                
                
                
                
                
                if (!heightShrinkWrap && constrain && childContext[names.heightModel].shrinkWrap && childHeight > availHeight) {
                    childContext.invalidate({
                        before: onBeforeInvalidateChild,
                        after: onAfterInvalidateChild,
                        layout: me,
                        childHeight: availHeight,
                        names: names
                    });
                    
                    
                    
                    ownerContext.state.parallelDone = false; 
                }

                
                if (isNaN(maxHeight = mmax(maxHeight, childHeight + childMargins,
                                           childContext.target[names.minHeight] || 0))) {
                    return false; 
                }
            }

            
            
            if (shrinkWrapParallelOverflow) {
                maxHeight += scrollbarHeight;
                ownerContext[names.hasOverflowX] = true;

                
                ownerContext.target.componentLayout[names.setHeightInDom] = true;

                
                
                
                
                ownerContext[names.invalidateScrollX] = Ext.isStrict && Ext.isIE8;
            }

            
            
            stretchMaxPartner = ownerContext.stretchMaxPartner;
            if (stretchMaxPartner) {
                
                ownerContext.setProp('maxChildHeight', maxHeight);
                stretchMaxChildren = stretchMaxPartner.childItems;
                
                if (stretchMaxChildren && stretchMaxChildren.length) {
                    maxHeight = mmax(maxHeight, stretchMaxPartner.getProp('maxChildHeight'));
                    if (isNaN(maxHeight)) {
                        return false;
                    }
                }
            }

            ownerContext[names.setContentHeight](maxHeight + me.padding[heightName] +
                    ownerContext.targetContext.getPaddingInfo()[heightName]);

            
            
            
            if (shrinkWrapParallelOverflow) {
                maxHeight -= scrollbarHeight;
            }
            plan.maxSize = maxHeight;

            if (isStretchMax) {
                height = maxHeight;
            } else if (isCenter || isBottom || hasPercentageSizes) {
                if (constrain) {
                    height = heightShrinkWrap ? maxHeight : availHeight;
                } else {
                    height = heightShrinkWrap ? maxHeight : mmax(availHeight, maxHeight);
                }

                
                
                
                
                height -= ownerContext.innerCtContext.getBorderInfo()[heightName];
            }
        }

        for (i = 0; i < childItemsLength; i++) {
            childContext = childItems[i];
            childMargins = childContext.marginInfo || childContext.getMarginInfo();

            childTop = top + childMargins[beforeYName];

            if (isStretch) {
                childContext[setHeightName](height - childMargins[heightName]);
            } else {
                percentagePerpendicular = childContext.percentagePerpendicular;
                if (heightShrinkWrap && percentagePerpendicular) {
                    childMargins = childContext.marginInfo || childContext.getMarginInfo();
                    childHeight = percentagePerpendicular * height - childMargins[heightName];
                    childHeight = childContext.setHeight(childHeight);
                }

                if (isCenter) {
                    diff = height - childContext.props[heightName];
                    if (diff > 0) {
                        childTop = top + Math[me.alignRoundingMethod](diff / 2);
                    }
                } else if (isBottom) {
                    childTop = mmax(0, height - childTop - childContext.props[heightName]);
                }
            }

            childContext.setProp(topPositionName, childTop);
        }

        return true;
    },
    
    onBeforeConstrainInvalidateChild: function(childContext, options){
        
        var heightModelName = options.names.heightModel;
        if (!childContext[heightModelName].constrainedMin) {
            
            
            childContext[heightModelName] = Ext.layout.SizeModel.calculated;
        }
    },
    
    onAfterConstrainInvalidateChild: function(childContext, options){
         
        var names = options.names;

        
        
        
        
        
        childContext.setProp(names.beforeY, 0);
        if (childContext[names.heightModel].calculated) {
            childContext[names.setHeight](options.childHeight);
        }
    },

    calculateStretchMax: function (ownerContext, names, plan) {
        var me = this,
            heightName = names.height,
            widthName = names.width,
            childItems = ownerContext.childItems,
            length = childItems.length,
            height = plan.maxSize,
            onBeforeStretchMaxInvalidateChild = me.onBeforeStretchMaxInvalidateChild,
            onAfterStretchMaxInvalidateChild = me.onAfterStretchMaxInvalidateChild,
            childContext, props, i, childHeight;

        for (i = 0; i < length; ++i) {
            childContext = childItems[i];

            props = childContext.props;
            childHeight = height - childContext.getMarginInfo()[heightName];

            if (childHeight != props[heightName] ||   
                childContext[names.heightModel].constrained) { 
                
                
                
                
                
                
                
                childContext.invalidate({
                    before: onBeforeStretchMaxInvalidateChild,
                    after: onAfterStretchMaxInvalidateChild,
                    layout: me,
                    
                    childWidth: props[widthName],
                    
                    childHeight: childHeight,
                    childX: props.x,
                    childY: props.y,
                    names: names
                });
            }
        }
    },
    
    onBeforeStretchMaxInvalidateChild: function (childContext, options) {
        
        var heightModelName = options.names.heightModel;

        
        
        
        
        if (!childContext[heightModelName].constrainedMax) {
            
            
            childContext[heightModelName] = Ext.layout.SizeModel.calculated;
        }
    },

    onAfterStretchMaxInvalidateChild: function (childContext, options) {
        
        var names = options.names,
            childHeight = options.childHeight,
            childWidth = options.childWidth;

        childContext.setProp('x', options.childX);
        childContext.setProp('y', options.childY);

        if (childContext[names.heightModel].calculated) {
            
            
            childContext[names.setHeight](childHeight);
        }

        if (childContext[names.widthModel].calculated) {
            childContext[names.setWidth](childWidth);
        }
    },

    completeLayout: function(ownerContext) {
        var me = this,
            names = ownerContext.boxNames,
            invalidateScrollX = ownerContext.invalidateScrollX,
            invalidateScrollY = ownerContext.invalidateScrollY,
            dom, el, overflowX, overflowY, styles;

        me.overflowHandler.completeLayout(ownerContext);

        if (invalidateScrollX || invalidateScrollY) {
            el = me.getTarget();
            dom = el.dom;
            styles = dom.style;

            if (invalidateScrollX) {
                
                overflowX = el.getStyle('overflowX');
                if (overflowX == 'auto') {
                    
                    overflowX = styles.overflowX;
                    styles.overflowX = 'scroll'; 
                } else {
                    invalidateScrollX = false; 
                }
            }

            if (invalidateScrollY) {
                
                overflowY = el.getStyle('overflowY');
                if (overflowY == 'auto') {
                    
                    overflowY = styles.overflowY;
                    styles.overflowY = 'scroll'; 
                } else {
                    invalidateScrollY = false; 
                }
            }

            if (invalidateScrollX || invalidateScrollY) { 
                
                dom.scrollWidth;

                if (invalidateScrollX) {
                    styles.overflowX = overflowX; 
                }
                if (invalidateScrollY) {
                    styles.overflowY = overflowY; 
                }
            }
        }

        
        if (me.scrollParallel) {
            me.owner.getTargetEl().dom[names.scrollLeft] = me.scrollPos;
        }
    },

    finishedLayout: function(ownerContext) {
        this.overflowHandler.finishedLayout(ownerContext);
        this.callParent(arguments);

        
        
        
        
        
        
        
        
        
        this.targetEl.setWidth(ownerContext.innerCtContext.props.width);
    },

    publishInnerCtSize: function(ownerContext, reservedSpace) {
        var me = this,
            names = ownerContext.boxNames,
            heightName = names.height,
            widthName = names.width,
            align = ownerContext.boxOptions.align,
            dock = me.owner.dock,
            padding = me.padding,
            plan = ownerContext.state.boxPlan,
            targetSize = plan.targetSize,
            height = targetSize[heightName],
            innerCtContext = ownerContext.innerCtContext,
            innerCtWidth = (ownerContext.parallelSizeModel.shrinkWrap || (plan.tooNarrow && me.scrollParallel)
                    ? ownerContext.state.contentWidth - ownerContext.targetContext.getPaddingInfo()[widthName]
                    : targetSize[widthName]) - (reservedSpace || 0),
            innerCtHeight;

        if (align.stretch) {
            innerCtHeight = height;
        } else {
            innerCtHeight = plan.maxSize + padding[names.beforeY] + padding[names.afterY] + innerCtContext.getBorderInfo()[heightName];

            if (!ownerContext.perpendicularSizeModel.shrinkWrap && (align.center || align.bottom)) {
                innerCtHeight = Math.max(height, innerCtHeight);
            }
        }

        innerCtContext[names.setWidth](innerCtWidth);
        innerCtContext[names.setHeight](innerCtHeight);

        
        if (isNaN(innerCtWidth + innerCtHeight)) {
            me.done = false;
        }

        
        
        
        
        
        
        if (plan.calculatedWidth && (dock == 'left' || dock == 'right')) {
            
            ownerContext.setWidth(plan.calculatedWidth, true, true);
        }
    },

    onRemove: function(comp, isDestroying){
        var me = this,
            names = me.names,
            el;
            
        me.callParent(arguments);
        if (me.overflowHandler) {
            me.overflowHandler.onRemove(comp);
        }
        if (comp.layoutMarginCap == me.id) {
            delete comp.layoutMarginCap;
        }
        
        if (!me.owner.destroying && !isDestroying && comp.rendered) {
            
            el = comp.getEl();
            el.setStyle(names.beforeY, '');
            el.setStyle(names.beforeX, '');
        }
    },

    
    initOverflowHandler: function() {
        var me = this,
            handler = me.overflowHandler,
            handlerType,
            constructor;

        if (typeof handler == 'string') {
            handler = {
                type: handler
            };
        }

        handlerType = 'None';
        if (handler && handler.type !== undefined) {
            handlerType = handler.type;
        }

        constructor = Ext.layout.container.boxOverflow[handlerType];
        if (constructor[me.type]) {
            constructor = constructor[me.type];
        }

        me.overflowHandler = Ext.create('Ext.layout.container.boxOverflow.' + handlerType, me, handler);
    },

    
    
    getRenderTarget: function() {
        return this.targetEl;
    },

    
    
    getElementTarget: function() {
        return this.innerCt;
    },


    
    destroy: function() {
        var me = this;
        Ext.destroy(me.innerCt, me.overflowHandler);
        me.flexSortFn = me.innerCt = null;
        me.callParent(arguments);
    },

    getRenderData: function() {
        var data = this.callParent();

        data.targetElCls = this.targetElCls;

        return data;
    }
});


Ext.define('Ext.layout.container.HBox', {

    

    alias: ['layout.hbox'],
    extend:  Ext.layout.container.Box ,
    alternateClassName: 'Ext.layout.HBoxLayout',

    

    
    align: 'top', 

    
    
    
    constrainAlign: false,

    type : 'hbox',

    direction: 'horizontal',

    horizontal: true,

    names: {
        
        beforeX: 'left',
        beforeScrollX: 'left',
        beforeScrollerSuffix: '-before-scroller',
        afterScrollerSuffix: '-after-scroller',
        leftCap: 'Left',
        afterX: 'right',
        width: 'width',
        contentWidth: 'contentWidth',
        minWidth: 'minWidth',
        maxWidth: 'maxWidth',
        widthCap: 'Width',
        widthModel: 'widthModel',
        widthIndex: 0,
        x: 'x',
        scrollLeft: 'scrollLeft',
        overflowX: 'overflowX',
        hasOverflowX: 'hasOverflowX',
        invalidateScrollX: 'invalidateScrollX',
        parallelMargins: 'lr',

        
        center: 'middle',
        beforeY: 'top',
        afterY: 'bottom',
        height: 'height',
        contentHeight: 'contentHeight',
        minHeight: 'minHeight',
        maxHeight: 'maxHeight',
        heightCap: 'Height',
        heightModel: 'heightModel',
        heightIndex: 1,
        y: 'y',
        overflowY: 'overflowY',
        hasOverflowY: 'hasOverflowY',
        invalidateScrollY: 'invalidateScrollY',
        perpendicularMargins: 'tb',

        
        getWidth: 'getWidth',
        getHeight: 'getHeight',
        setWidth: 'setWidth',
        setHeight: 'setHeight',
        gotWidth: 'gotWidth',
        gotHeight: 'gotHeight',
        setContentWidth: 'setContentWidth',
        setContentHeight: 'setContentHeight',
        setWidthInDom: 'setWidthInDom',
        setHeightInDom: 'setHeightInDom',
        getScrollLeft: 'getScrollLeft',
        setScrollLeft: 'setScrollLeft',
        scrollTo: 'scrollTo'
    },

    sizePolicy: {
        flex: {
            '': {
                readsWidth : 0,
                readsHeight: 1,
                setsWidth  : 1,
                setsHeight : 0
            },
            stretch: {
                readsWidth : 0,
                readsHeight: 0,
                setsWidth  : 1,
                setsHeight : 1
            },
            stretchmax: {
                readsWidth : 0,
                readsHeight: 1,
                setsWidth  : 1,
                setsHeight : 1
            }
        },
        '': {
            readsWidth : 1,
            readsHeight: 1,
            setsWidth  : 0,
            setsHeight : 0
        },
        stretch: {
            readsWidth : 1,
            readsHeight: 0,
            setsWidth  : 0,
            setsHeight : 1
        },
        stretchmax: {
            readsWidth : 1,
            readsHeight: 1,
            setsWidth  : 0,
            setsHeight : 1
        }
    }            
});


Ext.define('Ext.layout.container.VBox', {

    

    alias: ['layout.vbox'],
    extend:  Ext.layout.container.Box ,
    alternateClassName: 'Ext.layout.VBoxLayout',

    

    
    align : 'left', 

    
    
    
    constrainAlign: false,

    type: 'vbox',

    direction: 'vertical',

    horizontal: false,

    names: {
        
        beforeX: 'top',
        beforeScrollX: 'top',
        beforeScrollerSuffix: '-before-scroller',
        afterScrollerSuffix: '-after-scroller',
        leftCap: 'Top',
        afterX: 'bottom',
        width: 'height',
        contentWidth: 'contentHeight',
        minWidth: 'minHeight',
        maxWidth: 'maxHeight',
        widthCap: 'Height',
        widthModel: 'heightModel',
        widthIndex: 1,
        x: 'y',
        scrollLeft: 'scrollTop',
        overflowX: 'overflowY',
        hasOverflowX: 'hasOverflowY',
        invalidateScrollX: 'invalidateScrollY',
        parallelMargins: 'tb',

        
        center: 'center',
        beforeY: 'left',
        afterY: 'right',
        height: 'width',
        contentHeight: 'contentWidth',
        minHeight: 'minWidth',
        maxHeight: 'maxWidth',
        heightCap: 'Width',
        heightModel: 'widthModel',
        heightIndex: 0,
        y: 'x',
        overflowY: 'overflowX',
        hasOverflowY: 'hasOverflowX',
        invalidateScrollY: 'invalidateScrollX',
        perpendicularMargins: 'lr',

        
        getWidth: 'getHeight',
        getHeight: 'getWidth',
        setWidth: 'setHeight',
        setHeight: 'setWidth',
        gotWidth: 'gotHeight',
        gotHeight: 'gotWidth',
        setContentWidth: 'setContentHeight',
        setContentHeight: 'setContentWidth',
        setWidthInDom: 'setHeightInDom',
        setHeightInDom: 'setWidthInDom',
        getScrollLeft: 'getScrollTop',
        setScrollLeft: 'setScrollTop',
        scrollTo: 'scrollTo'
    },

    sizePolicy: {
        flex: {
            '': {
                readsWidth : 1,
                readsHeight: 0,
                setsWidth  : 0,
                setsHeight : 1
            },
            stretch: {
                readsWidth : 0,
                readsHeight: 0,
                setsWidth  : 1,
                setsHeight : 1
            },
            stretchmax: {
                readsWidth : 1,
                readsHeight: 0,
                setsWidth  : 1,
                setsHeight : 1
            }
        },
        '': {
            readsWidth : 1,
            readsHeight: 1,
            setsWidth  : 0,
            setsHeight : 0
        },
        stretch: {
            readsWidth : 0,
            readsHeight: 1,
            setsWidth  : 1,
            setsHeight : 0
        },
        stretchmax: {
            readsWidth : 1,
            readsHeight: 1,
            setsWidth  : 1,
            setsHeight : 0
        }
    }
});


Ext.define('Ext.toolbar.Toolbar', {
    extend:  Ext.container.Container ,
               
                           
                                    
                                   
      
           
                               
      
    alias: 'widget.toolbar',
    alternateClassName: 'Ext.Toolbar',

    
    isToolbar: true,
    baseCls  : Ext.baseCSSPrefix + 'toolbar',
    ariaRole : 'toolbar',

    defaultType: 'button',

    
    vertical: false,

    

    
    enableOverflow: false,

    
    menuTriggerCls: Ext.baseCSSPrefix + 'toolbar-more-icon',

    

    
    trackMenus: true,

    itemCls: Ext.baseCSSPrefix + 'toolbar-item',

    statics: {
        shortcuts: {
            '-' : 'tbseparator',
            ' ' : 'tbspacer'
        },

        shortcutsHV: {
            
            0: {
                '->': { xtype: 'tbfill', height: 0 }
            },
            
            1: {
                '->': { xtype: 'tbfill', width: 0 }
            }
        }
    },

    initComponent: function() {
        var me = this;

        
        if (!me.layout && me.enableOverflow) {
            me.layout = { overflowHandler: 'Menu' };
        }

        if (me.dock === 'right' || me.dock === 'left') {
            me.vertical = true;
        }

        me.layout = Ext.applyIf(Ext.isString(me.layout) ? {
            type: me.layout
        } : me.layout || {}, {
            type: me.vertical ? 'vbox' : 'hbox',
            align: me.vertical ? 'stretchmax' : 'middle'
        });

        if (me.vertical) {
            me.addClsWithUI('vertical');
        }

        
        if (me.ui === 'footer') {
            me.ignoreBorderManagement = true;
        }

        me.callParent();

        
        me.addEvents('overflowchange');
    },

    getRefItems: function(deep) {
        var me = this,
            items = me.callParent(arguments),
            layout = me.layout,
            handler;

        if (deep && me.enableOverflow) {
            handler = layout.overflowHandler;
            if (handler && handler.menu) {
                items = items.concat(handler.menu.getRefItems(deep));
            }
        }
        return items;
    },

    

    

    
    lookupComponent: function(c) {
        var args = arguments;
        if (typeof c == 'string') {
            var T = Ext.toolbar.Toolbar,
                shortcut = T.shortcutsHV[this.vertical ? 1 : 0][c] || T.shortcuts[c];

            if (typeof shortcut == 'string') {
                c = {
                    xtype: shortcut
                };
            } else if (shortcut) {
                c = Ext.apply({}, shortcut);
            } else {
                c = {
                    xtype: 'tbtext',
                    text: c
                };
            }

            this.applyDefaults(c);

            
            args = [c];
        }

        return this.callParent(args);
    },

    
    applyDefaults: function(c) {
        if (!Ext.isString(c)) {
            c = this.callParent(arguments);
        }
        return c;
    },

    
    trackMenu: function(item, remove) {
        if (this.trackMenus && item.menu) {
            var method = remove ? 'mun' : 'mon',
                me = this;

            me[method](item, 'mouseover', me.onButtonOver, me);
            me[method](item, 'menushow', me.onButtonMenuShow, me);
            me[method](item, 'menuhide', me.onButtonMenuHide, me);
        }
    },

    
    onBeforeAdd: function(component) {
        var me = this,
            isButton = component.isButton;

        if (isButton && me.defaultButtonUI && component.ui === 'default' &&
            !component.hasOwnProperty('ui')) {
            component.ui = me.defaultButtonUI;
        } else if ((isButton || component.isFormField) && me.ui !== 'footer') {
            component.ui = component.ui + '-toolbar';
            component.addCls(component.baseCls + '-toolbar');
        }

        
        if (component instanceof Ext.toolbar.Separator) {
            component.setUI((me.vertical) ? 'vertical' : 'horizontal');
        }

        me.callParent(arguments);
    },

    
    onAdd: function(component) {
        this.callParent(arguments);
        this.trackMenu(component);
    },

    
    onRemove: function(c) {
        this.callParent(arguments);
        this.trackMenu(c, true);
    },

    getChildItemsToDisable: function() {
        return this.items.getRange();
    },

    
    onButtonOver: function(btn){
        if (this.activeMenuBtn && this.activeMenuBtn != btn) {
            this.activeMenuBtn.hideMenu();
            btn.showMenu();
            this.activeMenuBtn = btn;
        }
    },

    
    onButtonMenuShow: function(btn) {
        this.activeMenuBtn = btn;
    },

    
    onButtonMenuHide: function(btn) {
        delete this.activeMenuBtn;
    }
});


Ext.define('Ext.layout.component.Dock', {

    

    extend:  Ext.layout.component.Component ,

    alias: 'layout.dock',

    alternateClassName: 'Ext.layout.component.AbstractDock',

    

    type: 'dock',
    
    horzAxisProps: {
        name: 'horz',
        oppositeName: 'vert',
        dockBegin: 'left',
        dockEnd: 'right',
        horizontal: true,
        marginBegin: 'margin-left',
        maxSize: 'maxWidth',
        minSize: 'minWidth',
        pos: 'x',
        setSize: 'setWidth',
        shrinkWrapDock: 'shrinkWrapDockWidth',
        size: 'width',
        sizeModel: 'widthModel'
    },

    vertAxisProps: {
        name: 'vert',
        oppositeName: 'horz',
        dockBegin: 'top',
        dockEnd: 'bottom',
        horizontal: false,
        marginBegin: 'margin-top',
        maxSize: 'maxHeight',
        minSize: 'minHeight',
        pos: 'y',
        setSize: 'setHeight',
        shrinkWrapDock: 'shrinkWrapDockHeight',
        size: 'height',
        sizeModel: 'heightModel'
    },

    initializedBorders: -1,

    horizontalCollapsePolicy: { width: true, x: true },

    verticalCollapsePolicy: { height: true, y: true },

    finishRender: function () {
        var me = this,
            target, items;

        me.callParent();

        target = me.getRenderTarget();
        items = me.getDockedItems();

        me.finishRenderItems(target, items);
    },

    isItemBoxParent: function (itemContext) {
        return true;
    },

    isItemShrinkWrap: function (item) {
        return true;
    },

    noBorderClasses: [
        Ext.baseCSSPrefix + 'docked-noborder-top',
        Ext.baseCSSPrefix + 'docked-noborder-right',
        Ext.baseCSSPrefix + 'docked-noborder-bottom',
        Ext.baseCSSPrefix + 'docked-noborder-left'
    ],

    noBorderClassesSides: {
        top: Ext.baseCSSPrefix + 'docked-noborder-top',
        right: Ext.baseCSSPrefix + 'docked-noborder-right',
        bottom: Ext.baseCSSPrefix + 'docked-noborder-bottom',
        left: Ext.baseCSSPrefix + 'docked-noborder-left'
    },

    borderWidthProps: {
        top: 'border-top-width',
        right: 'border-right-width',
        bottom: 'border-bottom-width',
        left: 'border-left-width'
    },

    handleItemBorders: function() {
        var me = this,
            owner = me.owner,
            borders, docked,
            lastItems = me.lastDockedItems,
            oldBorders = me.borders,
            currentGeneration = owner.dockedItems.generation,
            noBorderClassesSides = me.noBorderClassesSides,
            borderWidthProps = me.borderWidthProps,
            i, ln, item, dock, side,
            collapsed = me.collapsed;

        if (me.initializedBorders == currentGeneration || (owner.border && !owner.manageBodyBorders)) {
            return;
        }

        me.initializedBorders = currentGeneration;

        
        me.collapsed = false;
        me.lastDockedItems = docked = me.getLayoutItems();
        me.collapsed = collapsed;

        borders = { top: [], right: [], bottom: [], left: [] };

        for (i = 0, ln = docked.length; i < ln; i++) {
            item = docked[i];
            dock = item.dock;

            if (item.ignoreBorderManagement) {
                continue;
            }

            if (!borders[dock].satisfied) {
                borders[dock].push(item);
                borders[dock].satisfied = true;
            }

            if (!borders.top.satisfied && dock !== 'bottom') {
                borders.top.push(item);
            }
            if (!borders.right.satisfied && dock !== 'left') {
                borders.right.push(item);
            }
            if (!borders.bottom.satisfied && dock !== 'top') {
                borders.bottom.push(item);
            }
            if (!borders.left.satisfied && dock !== 'right') {
                borders.left.push(item);
            }
        }

        if (lastItems) {
            for (i = 0, ln = lastItems.length; i < ln; i++) {
                item = lastItems[i];
                if (!item.isDestroyed && !item.ignoreBorderManagement && !owner.manageBodyBorders) {
                    item.removeCls(me.noBorderClasses);
                }
            }
        }

        if (oldBorders) {
            for (side in oldBorders) {
                if (owner.manageBodyBorders && oldBorders[side].satisfied) {
                    owner.setBodyStyle(borderWidthProps[side], '');
                }
            }
        }

        for (side in borders) {
            ln = borders[side].length;
            if (!owner.manageBodyBorders) {
                for (i = 0; i < ln; i++) {
                    borders[side][i].addCls(noBorderClassesSides[side]);
                }
                if ((!borders[side].satisfied && !owner.bodyBorder) || owner.bodyBorder === false) {
                    owner.addBodyCls(noBorderClassesSides[side]);
                }
            }
            else if (borders[side].satisfied) {
                owner.setBodyStyle(borderWidthProps[side], '1px');
            }
        }

        me.borders = borders;
    },

    beforeLayoutCycle: function (ownerContext) {
        var me = this,
            owner = me.owner,
            shrinkWrap = me.sizeModels.shrinkWrap,
            shrinkWrapDock = owner.shrinkWrapDock,
            collapsedHorz, collapsedVert;

        if (owner.collapsed) {
            if (owner.collapsedVertical()) {
                collapsedVert = true;
                ownerContext.measureDimensions = 1;
            } else {
                collapsedHorz = true;
                ownerContext.measureDimensions = 2;
            }
        }

        ownerContext.collapsedVert = collapsedVert;
        ownerContext.collapsedHorz = collapsedHorz;

        
        
        
        
        if (collapsedVert) {
            ownerContext.heightModel = shrinkWrap;
        } else if (collapsedHorz) {
            ownerContext.widthModel = shrinkWrap;
        }
        
        shrinkWrapDock = shrinkWrapDock === true ? 3 : (shrinkWrapDock || 0);
        ownerContext.shrinkWrapDockHeight = (shrinkWrapDock & 1) && ownerContext.heightModel.shrinkWrap;
        ownerContext.shrinkWrapDockWidth = (shrinkWrapDock & 2) && ownerContext.widthModel.shrinkWrap;
    },

    beginLayout: function(ownerContext) {
        var me = this,
            owner = me.owner,
            docked = me.getLayoutItems(),
            layoutContext = ownerContext.context,
            dockedItemCount = docked.length,
            dockedItems, i, item, itemContext, offsets,
            collapsed, dock;

        me.callParent(arguments);

        
        
        collapsed = owner.getCollapsed();
        if (collapsed !== me.lastCollapsedState && Ext.isDefined(me.lastCollapsedState)) {
            
            if (me.owner.collapsed) {
                ownerContext.isCollapsingOrExpanding = 1;
                
                owner.addClsWithUI(owner.collapsedCls);
            } else {
                ownerContext.isCollapsingOrExpanding = 2;
                
                owner.removeClsWithUI(owner.collapsedCls);
                ownerContext.lastCollapsedState = me.lastCollapsedState;
            }
        }
        me.lastCollapsedState = collapsed;

        ownerContext.dockedItems = dockedItems = [];

        for (i = 0; i < dockedItemCount; i++) {
            item = docked[i];
            if (item.rendered) {
                dock = item.dock;
                itemContext = layoutContext.getCmp(item);
                itemContext.dockedAt = { x: 0, y: 0 };
                itemContext.offsets = offsets = Ext.Element.parseBox(item.offsets || 0);
                itemContext.horizontal = dock == 'top' || dock == 'bottom';
                offsets.width = offsets.left + offsets.right;
                offsets.height = offsets.top + offsets.bottom;
                dockedItems.push(itemContext);
            }
        }

        ownerContext.bodyContext = ownerContext.getEl('body');
    },

    beginLayoutCycle: function(ownerContext) {
        var me = this,
            docked = ownerContext.dockedItems,
            len = docked.length,
            owner = me.owner,
            frameBody = owner.frameBody,
            lastHeightModel = me.lastHeightModel,
            i, item, dock;

        me.callParent(arguments);

        if (me.owner.manageHeight) {
            
            
            if (me.lastBodyDisplay) {
                owner.body.dom.style.display = me.lastBodyDisplay = '';
            }
        } else {
            
            
            
            if (me.lastBodyDisplay !== 'inline-block') {
                owner.body.dom.style.display = me.lastBodyDisplay = 'inline-block';
            }

            if (lastHeightModel && lastHeightModel.shrinkWrap &&
                        !ownerContext.heightModel.shrinkWrap) {
                owner.body.dom.style.marginBottom = '';
            }
        }

        if (ownerContext.widthModel.auto) {
            if (ownerContext.widthModel.shrinkWrap) {
                owner.el.setWidth(null);
            }
            owner.body.setWidth(null);
            if (frameBody) {
                frameBody.setWidth(null);
            }
        }
        if (ownerContext.heightModel.auto) {
            owner.body.setHeight(null);
            
            if (frameBody) {
                frameBody.setHeight(null);
            }
        }

        
        
        if (ownerContext.collapsedVert) {
            ownerContext.setContentHeight(0);
        } else if (ownerContext.collapsedHorz) {
            ownerContext.setContentWidth(0);
        }

        
        
        for (i = 0; i < len; i++) {
            item = docked[i].target;
            dock = item.dock;

            if (dock == 'right') {
                item.setLocalX(0);
            } else if (dock != 'left') {
                continue;
            }

            
        }
    },

    calculate: function (ownerContext) {
        var me = this,
            measure = me.measureAutoDimensions(ownerContext, ownerContext.measureDimensions),
            state = ownerContext.state,
            horzDone = state.horzDone,
            vertDone = state.vertDone,
            bodyContext = ownerContext.bodyContext,
            framing, horz, vert, forward, backward;

        
        ownerContext.borderInfo  || ownerContext.getBorderInfo();
        ownerContext.paddingInfo || ownerContext.getPaddingInfo();
        ownerContext.frameInfo   || ownerContext.getFrameInfo();
        bodyContext.borderInfo   || bodyContext.getBorderInfo();
        bodyContext.paddingInfo  || bodyContext.getPaddingInfo();

        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        if (!ownerContext.frameBorder) {
            if (!(framing = ownerContext.framing)) {
                ownerContext.frameBorder = ownerContext.borderInfo;
                ownerContext.framePadding = ownerContext.paddingInfo;
            } else {
                
                ownerContext.frameBorder = framing.border;
                ownerContext.framePadding = framing.padding;
            }
        }

        
        
        horz = !horzDone &&
               me.createAxis(ownerContext, measure.contentWidth, ownerContext.widthModel,
                             me.horzAxisProps, ownerContext.collapsedHorz);
        vert = !vertDone &&
               me.createAxis(ownerContext, measure.contentHeight, ownerContext.heightModel,
                             me.vertAxisProps, ownerContext.collapsedVert);

        
        
        
        
        
        
        
        
        for (forward = 0, backward = ownerContext.dockedItems.length; backward--; ++forward) {
            if (horz) {
                me.dockChild(ownerContext, horz, backward, forward);
            }
            if (vert) {
                me.dockChild(ownerContext, vert, backward, forward);
            }
        }
        
        if (horz && me.finishAxis(ownerContext, horz)) {
            state.horzDone = horzDone = horz;
        }
        
        if (vert && me.finishAxis(ownerContext, vert)) {
            state.vertDone = vertDone = vert;
        }

        
        
        if (horzDone && vertDone && me.finishConstraints(ownerContext, horzDone, vertDone)) {
            
            
            
            me.finishPositions(ownerContext, horzDone, vertDone);
        } else {
            me.done = false;
        }
    },

    
    createAxis: function (ownerContext, contentSize, sizeModel, axisProps, collapsedAxis) {
        var me = this,
            begin = 0,
            owner = me.owner,
            maxSize = owner[axisProps.maxSize],
            minSize = owner[axisProps.minSize] || 0,
            dockBegin = axisProps.dockBegin,
            dockEnd = axisProps.dockEnd,
            posProp = axisProps.pos,
            sizeProp = axisProps.size,
            hasMaxSize = maxSize != null, 
            shrinkWrap = sizeModel.shrinkWrap,
            bodyContext, framing, padding, end;

        if (shrinkWrap) {
            
            
            if (collapsedAxis) {
                end = 0;
            } else {
                bodyContext = ownerContext.bodyContext;
                end = contentSize + bodyContext.borderInfo[sizeProp];
            }
        } else {
            framing = ownerContext.frameBorder;
            padding = ownerContext.framePadding;

            begin = framing[dockBegin] + padding[dockBegin];
            end = ownerContext.getProp(sizeProp) - (framing[dockEnd] + padding[dockEnd]);
        }

        return {
            shrinkWrap: sizeModel.shrinkWrap,
            sizeModel: sizeModel,
            
            initialBegin: begin,
            begin: begin,
            end: end,
            collapsed: collapsedAxis,
            horizontal: axisProps.horizontal,
            ignoreFrameBegin: null,
            ignoreFrameEnd: null,
            initialSize: end - begin,
            maxChildSize: 0,
            hasMinMaxConstraints: (minSize || hasMaxSize) && sizeModel.shrinkWrap,
            minSize: minSize,
            maxSize: hasMaxSize ? maxSize : 1e9,
            bodyPosProp: me.owner.manageHeight ? posProp : axisProps.marginBegin,
            dockBegin: dockBegin,    
            dockEnd: dockEnd,        
            posProp: posProp,        
            sizeProp: sizeProp,      
            setSize: axisProps.setSize,
            shrinkWrapDock: ownerContext[axisProps.shrinkWrapDock],
            sizeModelName: axisProps.sizeModel,
            dockedPixelsEnd: 0
        };
    },

    
    dockChild: function (ownerContext, axis, backward, forward) {
        var me = this,
            itemContext = ownerContext.dockedItems[axis.shrinkWrap ? backward : forward],
            item = itemContext.target,
            dock = item.dock, 
            sizeProp = axis.sizeProp,
            pos, size;

        if (item.ignoreParentFrame && ownerContext.isCollapsingOrExpanding) {
            
            
            itemContext.clearMarginCache();
        }

        itemContext.marginInfo || itemContext.getMarginInfo(); 

        if (dock == axis.dockBegin) {
            if (axis.shrinkWrap) {
                pos = me.dockOutwardBegin(ownerContext, itemContext, item, axis);
            } else {
                pos = me.dockInwardBegin(ownerContext, itemContext, item, axis);
            }
        } else if (dock == axis.dockEnd) {
            if (axis.shrinkWrap) {
                pos = me.dockOutwardEnd(ownerContext, itemContext, item, axis);
            } else {
                pos = me.dockInwardEnd(ownerContext, itemContext, item, axis);
            }
        } else {
            if (axis.shrinkWrapDock) {
                
                
                size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp];
                axis.maxChildSize = Math.max(axis.maxChildSize, size);
                pos = 0;
            } else {
                pos = me.dockStretch(ownerContext, itemContext, item, axis);
            }
        }

        itemContext.dockedAt[axis.posProp] = pos;
    },

    
    dockInwardBegin: function (ownerContext, itemContext, item, axis) {
        var pos = axis.begin,
            sizeProp = axis.sizeProp,
            ignoreParentFrame = item.ignoreParentFrame,
            delta,
            size, 
            dock;

        if (ignoreParentFrame) {
            axis.ignoreFrameBegin = itemContext;
            dock = item.dock;

            
            delta = ownerContext.frameBorder[dock];

            
            pos -= delta + ownerContext.framePadding[dock];
        }

        if (!item.overlay) {
            size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp];
            axis.begin += size;
            if (ignoreParentFrame) {
                axis.begin -= delta;
            }
        }

        return pos;
    },

    
    dockInwardEnd: function (ownerContext, itemContext, item, axis) {
        var sizeProp = axis.sizeProp,
            size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp],
            pos = axis.end - size,
            frameEnd;

        if (!item.overlay) {
            axis.end = pos;
        }

        if (item.ignoreParentFrame) {
            axis.ignoreFrameEnd = itemContext;
            frameEnd = ownerContext.frameBorder[item.dock];
            pos += frameEnd + ownerContext.framePadding[item.dock];
            axis.end += frameEnd;
        }

        return pos;
    },

    
    dockOutwardBegin: function (ownerContext, itemContext, item, axis) {
        var pos = axis.begin,
            sizeProp = axis.sizeProp,
            size;

        if (axis.collapsed) {
            axis.ignoreFrameBegin = axis.ignoreFrameEnd = itemContext;
        } else if (item.ignoreParentFrame) {
            axis.ignoreFrameBegin = itemContext;
        }
        
        

        if (!item.overlay) {
            size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp];
            pos -= size;
            axis.begin = pos;
        }

        return pos;
    },

    
    dockOutwardEnd: function (ownerContext, itemContext, item, axis) {
        var pos = axis.end,
            sizeProp = axis.sizeProp,
            size;

        size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp];

        if (axis.collapsed) {
            axis.ignoreFrameBegin = axis.ignoreFrameEnd = itemContext;
        } else if (item.ignoreParentFrame) {
            axis.ignoreFrameEnd = itemContext;
        }
        
        

        if (!item.overlay) {
            axis.end = pos + size;
            axis.dockedPixelsEnd += size;
        }

        return pos;
    },

    
    dockStretch: function (ownerContext, itemContext, item, axis) {
        var dock = item.dock, 
            sizeProp = axis.sizeProp, 
            horizontal = dock == 'top' || dock == 'bottom',
            border = ownerContext.frameBorder,
            offsets = itemContext.offsets,
            padding = ownerContext.framePadding,
            endProp = horizontal ? 'right' : 'bottom',
            startProp = horizontal ? 'left' : 'top',
            pos = axis.begin + offsets[startProp],
            margin, size;

        if (item.stretch !== false) {
            size = axis.end - pos - offsets[endProp];

            if (item.ignoreParentFrame) {
                
                
                
                
                pos -= padding[startProp] + border[startProp];
                size += padding[sizeProp] + border[sizeProp];
            }

            margin = itemContext.marginInfo;
            size -= margin[sizeProp];

            itemContext[axis.setSize](size);
        }

        return pos;
    },

    
    finishAxis: function (ownerContext, axis) {
        
        
        
        if (isNaN(axis.maxChildSize)) {
            return false;
        }
        
        var axisBegin = axis.begin,
            size = axis.end - axisBegin,
            collapsed = axis.collapsed,
            setSizeMethod = axis.setSize,
            beginName = axis.dockBegin, 
            endName = axis.dockEnd, 
            padding = ownerContext.framePadding,
            border = ownerContext.frameBorder,
            borderBegin = border[beginName],
            framing = ownerContext.framing,
            framingBegin = framing && framing[beginName],
            
            paddingBegin = collapsed ? 0 : padding[beginName],
            sizeProp = axis.sizeProp,
            ignoreFrameBegin = axis.ignoreFrameBegin,
            ignoreFrameEnd = axis.ignoreFrameEnd,
            bodyContext = ownerContext.bodyContext,
            extraPaddingBegin = Math.max(borderBegin + paddingBegin - framingBegin, 0),
            bodyPos, bodySize, delta, dirty;

        if (axis.shrinkWrap) {
            
            
            

            bodySize = axis.initialSize;

            if (framing) {
                
                
                
                
                
                
                

                delta = -axisBegin + borderBegin + paddingBegin;
                bodyPos = delta - framingBegin - extraPaddingBegin;
            } else {
                bodyPos = -axisBegin;
                delta = bodyPos + paddingBegin;
            }

            if (!collapsed) {
                size += padding[sizeProp];
            }

            if (ignoreFrameBegin) {
                
                
                
                delta -= borderBegin;
                bodyPos -= borderBegin;

                
                
                
                
                
                
                ignoreFrameBegin.dockedAt[axis.posProp] -= paddingBegin;
            } else {
                size += borderBegin;
            }

            if (collapsed) {
                
                
            } else if (ignoreFrameEnd) {
                
                
                
                ignoreFrameEnd.dockedAt[axis.posProp] += padding[endName];
            } else {
                size += border[endName];
            }

            axis.size = size; 

            if (!axis.horizontal && !this.owner.manageHeight) {
                
                
                dirty = false;
            }
        } else {
            
            
            if (framing) {
                
                
                delta = 0;
                bodyPos = axisBegin - framingBegin - extraPaddingBegin;
            } else {
                delta = -borderBegin;
                bodyPos = axisBegin - paddingBegin - borderBegin;
            }

            
            bodySize = size;
        }

        axis.delta = delta;
        bodyContext[setSizeMethod](bodySize, dirty);
        bodyContext.setProp(axis.bodyPosProp, bodyPos);

        return !isNaN(size);
    },
    
    beforeInvalidateShrinkWrapDock: function(itemContext, options){
        var sizeModelName = options.axis.sizeModelName;
        if (!itemContext[sizeModelName].constrainedMin) {
            
            
            itemContext[sizeModelName] = Ext.layout.SizeModel.calculated;
        }
    },
    
    afterInvalidateShrinkWrapDock: function(itemContext, options){
        var axis = options.axis,
            me = options.layout,
            pos;

        if (itemContext[axis.sizeModelName].calculated) {
            pos = me.dockStretch(options.ownerContext, itemContext, itemContext.target, axis);
            itemContext.setProp(axis.posProp, axis.delta + pos);
        }
    },
    
    
    finishConstraints: function (ownerContext, horz, vert) {
        var me = this,
            sizeModels = me.sizeModels,
            publishWidth = horz.shrinkWrap,
            publishHeight = vert.shrinkWrap,
            owner = me.owner,
            dirty, height, width, heightModel, widthModel, size, 
            minSize, maxSize, maxChildSize, desiredSize;

        
        
        
        
        
        if (publishWidth) {
            size = horz.size;
            minSize = horz.collapsed ? 0 : horz.minSize;
            maxSize = horz.maxSize;
            maxChildSize = horz.maxChildSize;
            desiredSize = Math.max(size, maxChildSize);

            if (desiredSize > maxSize) {
                widthModel = sizeModels.constrainedMax;
                width = maxSize;
            } else if (desiredSize < minSize) {
                widthModel = sizeModels.constrainedMin;
                width = minSize;
            } else if (size < maxChildSize) {
                widthModel = sizeModels.constrainedDock;
                owner.dockConstrainedWidth = width = maxChildSize;
            } else {
                width = size;
            }
        }

        if (publishHeight) {
            size = vert.size;
            minSize = vert.collapsed ? 0 : vert.minSize;
            maxSize = vert.maxSize;
            maxChildSize = vert.maxChildSize;
            
            
            desiredSize = Math.max(size, maxChildSize + size - vert.initialSize);

            if (desiredSize > maxSize) {
                heightModel = sizeModels.constrainedMax;
                height = maxSize;
            } else if (desiredSize < minSize) {
                heightModel = sizeModels.constrainedMin;
                height = minSize;
            } else if (size < maxChildSize) {
                heightModel = sizeModels.constrainedDock;
                owner.dockConstrainedHeight = height = maxChildSize;
            } else {
                if (!ownerContext.collapsedVert && !owner.manageHeight) {
                    
                    
                    dirty = false;

                    
                    ownerContext.bodyContext.setProp('margin-bottom', vert.dockedPixelsEnd);
                }

                height = size;
            }
        }

        

        if (widthModel || heightModel) {
            
            
            if (widthModel && heightModel &&
                        widthModel.constrainedMax &&  heightModel.constrainedByMin) {
                ownerContext.invalidate({ widthModel: widthModel });
                return false;
            }

            
            
            
            if (!ownerContext.widthModel.calculatedFromShrinkWrap &&
                        !ownerContext.heightModel.calculatedFromShrinkWrap) {
                
                ownerContext.invalidate({ widthModel: widthModel, heightModel: heightModel });
                return false;
            }

            
            
            
        } else {
            
            
            me.invalidateAxes(ownerContext, horz, vert);
            
        }

        

        if (publishWidth) {
            ownerContext.setWidth(width);
            if (widthModel) {
                ownerContext.widthModel = widthModel; 
            }
        }
        if (publishHeight) {
            ownerContext.setHeight(height, dirty);
            if (heightModel) {
                ownerContext.heightModel = heightModel; 
            }
        }

        return true;
    },
    
     
    invalidateAxes: function(ownerContext, horz, vert){
        var before = this.beforeInvalidateShrinkWrapDock,
            after = this.afterInvalidateShrinkWrapDock,
            horzSize = horz.end - horz.begin,
            vertSize = vert.initialSize,
            invalidateHorz = horz.shrinkWrapDock && horz.maxChildSize <= horzSize,
            invalidateVert = vert.shrinkWrapDock && vert.maxChildSize <= vertSize,
            dockedItems, len, i, itemContext, itemSize, isHorz, axis, sizeProp;

        if (invalidateHorz || invalidateVert) {
            if (invalidateVert) {
                
                
                vert.begin = vert.initialBegin;
                vert.end = vert.begin + vert.initialSize;
            }
            dockedItems = ownerContext.dockedItems;
            for (i = 0, len = dockedItems.length; i < len; ++i) {
                itemContext = dockedItems[i];
                isHorz = itemContext.horizontal;
                axis = null;
                if (invalidateHorz && isHorz) {
                    sizeProp = horz.sizeProp;
                    itemSize = horzSize;
                    axis = horz;
                } else if (invalidateVert && !isHorz) {
                    sizeProp = vert.sizeProp;
                    itemSize = vertSize;
                    axis = vert;
                }
                
                if (axis) {
                    
                    itemSize -= itemContext.getMarginInfo()[sizeProp];
                    if (itemSize !== itemContext.props[sizeProp]) {
                        itemContext.invalidate({
                            before: before,
                            after: after,
                            axis: axis,
                            ownerContext: ownerContext,
                            layout: this
                        });
                    }
                }
            }
        }
    },

    
    finishPositions: function (ownerContext, horz, vert) {
        var dockedItems = ownerContext.dockedItems,
            length = dockedItems.length,
            deltaX = horz.delta,
            deltaY = vert.delta,
            index, itemContext;

        for (index = 0; index < length; ++index) {
            itemContext = dockedItems[index];

            itemContext.setProp('x', deltaX + itemContext.dockedAt.x);
            itemContext.setProp('y', deltaY + itemContext.dockedAt.y);
        }
    },

    finishedLayout: function(ownerContext) {
        var me = this,
            target = ownerContext.target;

        me.callParent(arguments);

        if (!ownerContext.animatePolicy) {
            if (ownerContext.isCollapsingOrExpanding === 1) {
                target.afterCollapse(false);
            } else if (ownerContext.isCollapsingOrExpanding === 2) {
                target.afterExpand(false);
            }
        }
    },

    getAnimatePolicy: function(ownerContext) {
        var me = this,
            lastCollapsedState, policy;

        if (ownerContext.isCollapsingOrExpanding == 1) {
            lastCollapsedState = me.lastCollapsedState;
        } else if (ownerContext.isCollapsingOrExpanding == 2) {
            lastCollapsedState = ownerContext.lastCollapsedState;
        }

        if (lastCollapsedState == 'left' || lastCollapsedState == 'right') {
            policy = me.horizontalCollapsePolicy;
        } else if (lastCollapsedState == 'top' || lastCollapsedState == 'bottom') {
            policy = me.verticalCollapsePolicy;
        }

        return policy;
    },

    
    getDockedItems: function(order, beforeBody) {
        var me = this,
            renderedOnly = (order === 'visual'),
            all = renderedOnly ? Ext.ComponentQuery.query('[rendered]', me.owner.dockedItems.items) : me.owner.dockedItems.items,
            sort = all && all.length && order !== false,
            renderOrder,
            dock, dockedItems, i, isBefore, length;

        if (beforeBody == null) {
            dockedItems = sort && !renderedOnly ? all.slice() : all;
        } else {
            dockedItems = [];

            for (i = 0, length = all.length; i < length; ++i) {
                dock = all[i].dock;
                isBefore = (dock == 'top' || dock == 'left');
                if (beforeBody ? isBefore : !isBefore) {
                    dockedItems.push(all[i]);
                }
            }

            sort = sort && dockedItems.length;
        }

        if (sort) {
            renderOrder = (order = order || 'render') == 'render';
            Ext.Array.sort(dockedItems, function(a, b) {
                var aw,
                    bw;

                
                
                if (renderOrder && ((aw = me.owner.dockOrder[a.dock]) !== (bw = me.owner.dockOrder[b.dock]))) {

                    
                    if (!(aw + bw)) {
                        return aw - bw;
                    }
                }

                aw = me.getItemWeight(a, order);
                bw = me.getItemWeight(b, order);
                if ((aw !== undefined) && (bw !== undefined)) {
                    return aw - bw;
                }
                return 0;
            });
        }

        return dockedItems || [];
    },

    getItemWeight: function (item, order) {
        var weight = item.weight || this.owner.defaultDockWeights[item.dock];
        return weight[order] || weight;
    },

    
    getLayoutItems : function() {
        var me = this,
            items,
            itemCount,
            item,
            i,
            result;

        if (me.owner.collapsed) {
            result = me.owner.getCollapsedDockedItems();
        } else {
            items = me.getDockedItems('visual');
            itemCount = items.length;
            result = [];
            for (i = 0; i < itemCount; i++) {
                item = items[i];
                if (!item.hidden) {
                    result.push(item);
                }
            }
        }
        return result;
    },

    
    measureContentWidth: function (ownerContext) {
        var bodyContext = ownerContext.bodyContext;
        return bodyContext.el.getWidth() - bodyContext.getBorderInfo().width;
    },

    measureContentHeight: function (ownerContext) {
        var bodyContext = ownerContext.bodyContext;
        return bodyContext.el.getHeight() - bodyContext.getBorderInfo().height;
    },
    
    redoLayout: function(ownerContext) {
        var me = this,
            owner = me.owner;
        
        
        if (ownerContext.isCollapsingOrExpanding == 1) {
            if (owner.reExpander) {
                owner.reExpander.el.show();
            }
            
            owner.addClsWithUI(owner.collapsedCls);
            ownerContext.redo(true);
        } else if (ownerContext.isCollapsingOrExpanding == 2) {
            
            owner.removeClsWithUI(owner.collapsedCls);
            ownerContext.bodyContext.redo();
        } 
    },

    
    
    renderChildren: function() {
        var me = this,
            items = me.getDockedItems(),
            target = me.getRenderTarget();

        me.handleItemBorders();

        me.renderItems(items, target);
    },

    
    renderItems: function(items, target) {
        var me = this,
            dockedItemCount = items.length,
            itemIndex = 0,
            correctPosition = 0,
            staticNodeCount = 0,
            targetNodes = me.getRenderTarget().dom.childNodes,
            targetChildCount = targetNodes.length,
            i, j, targetChildNode, item;

        
        for (i = 0, j = 0; i < targetChildCount; i++) {
            targetChildNode = targetNodes[i];
            if (Ext.fly(targetChildNode).hasCls(Ext.baseCSSPrefix + 'resizable-handle')) {
                break;
            }
            for (j = 0; j < dockedItemCount; j++) {
                item = items[j];
                if (item.rendered && item.el.dom === targetChildNode) {
                    break;
                }
            }
            
            
            if (j === dockedItemCount) {
                staticNodeCount++;
            }
        }

        
        for (; itemIndex < dockedItemCount; itemIndex++, correctPosition++) {
            item = items[itemIndex];

            
            
            
            
            
            
            
            if (itemIndex === correctPosition && (item.dock === 'right' || item.dock === 'bottom')) {
                correctPosition += staticNodeCount;
            }

            
            if (item && !item.rendered) {
                me.renderItem(item, target, correctPosition);
            }
            else if (!me.isValidParent(item, target, correctPosition)) {
                me.moveItem(item, target, correctPosition);
            }
        }
    },

    undoLayout: function(ownerContext) {
        var me = this,
            owner = me.owner;
        
        
        if (ownerContext.isCollapsingOrExpanding == 1) {

            
            if (owner.reExpander) {
                owner.reExpander.el.hide();
            }
            
            owner.removeClsWithUI(owner.collapsedCls);
            ownerContext.undo(true);
        } else if (ownerContext.isCollapsingOrExpanding == 2) {
            
            owner.addClsWithUI(owner.collapsedCls);
            ownerContext.bodyContext.undo();
        } 
    },

    sizePolicy: {
        nostretch: {
            setsWidth: 0,
            setsHeight: 0
        },

        horz: { 
            shrinkWrap: {
                
                
                setsWidth: 1,
                setsHeight: 0,
                readsWidth: 1
            },
            stretch: {
                setsWidth: 1,
                setsHeight: 0
            }
        },

        vert: { 
            shrinkWrap: {
                setsWidth: 0,
                setsHeight: 1,
                readsHeight: 1
            },
            stretch: {
                setsWidth: 0,
                setsHeight: 1
            }
        },

        stretchV: {
            setsWidth: 0,
            setsHeight: 1
        },

        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        

        autoStretchH: {
            readsWidth: 1,
            setsWidth: 1,
            setsHeight: 0
        },
        autoStretchV: {
            readsHeight: 1,
            setsWidth: 0,
            setsHeight: 1
        }
    },

    getItemSizePolicy: function (item, ownerSizeModel) {
        var me = this,
            policy = me.sizePolicy,
            shrinkWrapDock = me.owner.shrinkWrapDock,
            dock, vertical;

        if (item.stretch === false) {
            return policy.nostretch;
        }

        dock = item.dock;
        vertical = (dock == 'left' || dock == 'right');

        shrinkWrapDock = shrinkWrapDock === true ? 3 : (shrinkWrapDock || 0);
        if (vertical) {
            policy = policy.vert;
            shrinkWrapDock = shrinkWrapDock & 1;
        } else {
            policy = policy.horz;
            shrinkWrapDock = shrinkWrapDock & 2;
        }

        if (shrinkWrapDock) {
            
            if (!ownerSizeModel) {
                ownerSizeModel = me.owner.getSizeModel();
            }
            if (ownerSizeModel[vertical ? 'height' : 'width'].shrinkWrap) {
                return policy.shrinkWrap;
            }
        }

        return policy.stretch;
    },

    
    configureItem : function(item, pos) {
        this.callParent(arguments);

        item.addCls(Ext.baseCSSPrefix + 'docked');
        item.addClsWithUI(this.getDockCls(item.dock));
    },

    
    getDockCls: function(dock) {
        return 'docked-' + dock;
    },

    afterRemove : function(item) {
        this.callParent(arguments);
        if (this.itemCls) {
            item.el.removeCls(this.itemCls + '-' + item.dock);
        }
        var dom = item.el.dom;

        if (!item.destroying && dom) {
            dom.parentNode.removeChild(dom);
        }
        this.childrenChanged = true;
    },

    
    borderCollapseMap: {
        
    },

    
    getBorderCollapseTable: function () {
        var me = this,
            map = me.borderCollapseMap,
            owner = me.owner,
            baseCls = owner.baseCls,
            ui = owner.ui,
            table;

        map = map[baseCls] || (map[baseCls] = {});
        table = map[ui];

        if (!table) {
            baseCls += '-' + ui + '-outer-border-';
            map[ui] = table = [
                0,                  
                baseCls + 'l',      
                baseCls + 'b',      
                baseCls + 'bl',     
                baseCls + 'r',      
                baseCls + 'rl',     
                baseCls + 'rb',     
                baseCls + 'rbl',    
                baseCls + 't',      
                baseCls + 'tl',     
                baseCls + 'tb',     
                baseCls + 'tbl',    
                baseCls + 'tr',     
                baseCls + 'trl',    
                baseCls + 'trb',    
                baseCls + 'trbl'    
            ];
        }

        return table;
    }
});


Ext.define('Ext.panel.AbstractPanel', {

    

    extend:  Ext.container.Container ,

    mixins: {
        docking:  Ext.container.DockingContainer 
    },

                                                                                 

    

    
    baseCls : Ext.baseCSSPrefix + 'panel',

    

    

    

    

    
    isPanel: true,
    
    

     
    contentPaddingProperty: 'bodyPadding',
    
    
    shrinkWrapDock: false,

    componentLayout: 'dock',

    childEls: [
        'body'
    ],

    renderTpl: [
        
        '{% this.renderDockedItems(out,values,0); %}',
        
        
        
        
        
        
        
        
        
        
        
        
        (Ext.isIE7m || Ext.isIEQuirks) ? '<div style="position:relative;font-size:0;line-height:0;" role="presentation">&nbsp;</div>' : '',
        '<div id="{id}-body" class="{baseCls}-body<tpl if="bodyCls"> {bodyCls}</tpl>',
            ' {baseCls}-body-{ui}<tpl if="uiCls">',
                '<tpl for="uiCls"> {parent.baseCls}-body-{parent.ui}-{.}</tpl>',
            '</tpl>{childElCls}"',
            '<tpl if="bodyRole"> role="{bodyRole}"<tpl else> role="presentation"</tpl>',
            '<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>>',
            '{%this.renderContainer(out,values);%}',
        '</div>',
        '{% this.renderDockedItems(out,values,1); %}'
    ],

    bodyPosProps: {
        x: 'x',
        y: 'y'
    },

    
    

    
    border: true,

    
    emptyArray: [],

    initComponent : function() {
        this.initBorderProps();
        this.callParent();
    },

    initBorderProps: function() {
        var me = this;

        if (me.frame && me.border && me.bodyBorder === undefined) {
            me.bodyBorder = false;
        }
        if (me.frame && me.border && (me.bodyBorder === false || me.bodyBorder === 0)) {
            me.manageBodyBorders = true;
        }
    },

    beforeDestroy: function(){
        this.destroyDockedItems();
        this.callParent();
    },

    
    initItems : function() {
        this.callParent();
        this.initDockingItems();
    },

    
    initRenderData: function() {
        var me = this,
            data = me.callParent();

        me.initBodyStyles();
        me.protoBody.writeTo(data);
        delete me.protoBody;

        return data;
    },

    
    getComponent: function(comp) {
        var component = this.callParent(arguments);
        if (component === undefined && !Ext.isNumber(comp)) {
            
            component = this.getDockedComponent(comp);
        }
        return component;
    },

    getProtoBody: function () {
        var me = this,
            body = me.protoBody;

        if (!body) {
            me.protoBody = body = new Ext.util.ProtoElement({
                cls: me.bodyCls,
                style: me.bodyStyle,
                clsProp: 'bodyCls',
                styleProp: 'bodyStyle',
                styleIsText: true
            });
        }

        return body;
    },

    
    initBodyStyles: function() {
        var me = this,
            body = me.getProtoBody();

        if (me.bodyPadding !== undefined) {
            if (me.layout.managePadding) {
                
                
                
                
                
                body.setStyle('padding', 0);
            } else {
                body.setStyle('padding', this.unitizeBox((me.bodyPadding === true) ? 5 : me.bodyPadding));
            }
        }
        me.initBodyBorder();
    },

    initBodyBorder: function() {
        var me = this;

        if (me.frame && me.bodyBorder) {
            if (!Ext.isNumber(me.bodyBorder)) {
                me.bodyBorder = 1;
            }
            me.getProtoBody().setStyle('border-width', this.unitizeBox(me.bodyBorder));
        }
    },

    getCollapsedDockedItems: function () {
        var me = this;
        return me.header === false || me.collapseMode == 'placeholder' ? me.emptyArray : [ me.getReExpander() ];
    },

    
    setBodyStyle: function(style, value) {
        var me = this,
            body = me.rendered ? me.body : me.getProtoBody();

        if (Ext.isFunction(style)) {
            style = style();
        }
        if (arguments.length == 1) {
            if (Ext.isString(style)) {
                style = Ext.Element.parseStyles(style);     
            }
            body.setStyle(style);
        } else {
            body.setStyle(style, value);
        }
        return me;
    },

    
    addBodyCls: function(cls) {
        var me = this,
            body = me.rendered ? me.body : me.getProtoBody();

        body.addCls(cls);
        return me;
    },

    
    removeBodyCls: function(cls) {
        var me = this,
            body = me.rendered ? me.body : me.getProtoBody();

        body.removeCls(cls);
        return me;
    },

    
    addUIClsToElement: function(cls) {
        var me = this,
            result = me.callParent(arguments);

        me.addBodyCls([Ext.baseCSSPrefix + cls, me.baseCls + '-body-' + cls, me.baseCls + '-body-' + me.ui + '-' + cls]);
        return result;
    },

    
    removeUIClsFromElement: function(cls) {
        var me = this,
            result = me.callParent(arguments);

        me.removeBodyCls([Ext.baseCSSPrefix + cls, me.baseCls + '-body-' + cls, me.baseCls + '-body-' + me.ui + '-' + cls]);
        return result;
    },

    
    addUIToElement: function() {
        var me = this;

        me.callParent(arguments);
        me.addBodyCls(me.baseCls + '-body-' + me.ui);
    },

    
    removeUIFromElement: function() {
        var me = this;

        me.callParent(arguments);
        me.removeBodyCls(me.baseCls + '-body-' + me.ui);
    },

    
    getTargetEl : function() {
        return this.body;
    },

    applyTargetCls: function(targetCls) {
        this.getProtoBody().addCls(targetCls);
    },

    getRefItems: function(deep) {
        var items = this.callParent(arguments);

        return this.getDockingRefItems(deep, items);
    },

    setupRenderTpl: function (renderTpl) {
        this.callParent(arguments);
        this.setupDockingRenderTpl(renderTpl);
    }
});


Ext.define('Ext.panel.Header', {
    extend:  Ext.container.Container ,
                                                                                     
    alias: 'widget.header',

    
    isHeader       : true,
    defaultType    : 'tool',
    indicateDrag   : false,
    weight         : -1,
    componentLayout: 'body',

    

    childEls: [
        'body'
    ],

    renderTpl: [
        '<div id="{id}-body" class="{headerCls}-body {baseCls}-body {bodyCls} {bodyTargetCls}',
        '<tpl for="uiCls"> {parent.baseCls}-body-{parent.ui}-{.}</tpl>"',
        '<tpl if="bodyStyle"> style="{bodyStyle}"</tpl> role="presentation">',
            '{%this.renderContainer(out,values)%}',
        '</div>'
    ],

    headingTpl: [
        
        '<span id="{id}-textEl" class="{headerCls}-text {cls}-text {cls}-text-{ui}" unselectable="on"',
            '<tpl if="headerRole">',
                ' role="{headerRole}"',
            '</tpl>',
        '>{title}</span>'
    ],

    shrinkWrap: 3,

    

    
    titlePosition: 0,

    

    

    

    
    headerCls: Ext.baseCSSPrefix + 'header',

    initComponent: function() {
        var me = this,
            hasPosition = me.hasOwnProperty('titlePosition'),
            items = me.items,
            titlePosition = hasPosition ? me.titlePosition : (items ? items.length : 0),
            uiClasses = [me.orientation, me.getDockName()],
            ownerCt = me.ownerCt;

        me.addEvents(
            
            'click',

            
            'dblclick'
        );

        me.indicateDragCls = me.headerCls + '-draggable';
        me.title = me.title || '&#160;';
        me.tools = me.tools || [];
        items = me.items = (items ? Ext.Array.slice(items) : []);
        me.orientation = me.orientation || 'horizontal';
        me.dock = (me.dock) ? me.dock : (me.orientation == 'horizontal') ? 'top' : 'left';

        
        if (ownerCt ? (ownerCt.border === false && !ownerCt.frame) : me.border === false) {
            uiClasses.push(me.orientation + '-noborder');
        }
        me.addClsWithUI(uiClasses);
        me.addCls([me.headerCls, me.headerCls + '-' + me.orientation]);

        if (me.indicateDrag) {
            me.addCls(me.indicateDragCls);
        }

        
        if (me.iconCls || me.icon || me.glyph) {
            me.initIconCmp();
            
            
            if (!hasPosition && !items.length) {
                ++titlePosition;
            }
            items.push(me.iconCmp);
        }

        
        me.titleCmp = new Ext.Component({
            ariaRole  : 'presentation',
            focusable : false,
            noWrap    : true,
            flex      : 1,
            rtl       : me.rtl,
            id        : me.id + '_hd',
            style     : me.titleAlign ? ('text-align:' + me.titleAlign) : '',
            cls       : me.headerCls + '-text-container ' +
                        me.baseCls + '-text-container ' +
                        me.baseCls + '-text-container-' + me.ui,
            renderTpl : me.getTpl('headingTpl'),
            renderData: {
                title: me.title,
                cls  : me.baseCls,
                headerCls: me.headerCls,
                headerRole: me.headerRole,
                ui   : me.ui
            },
            childEls  : ['textEl'],
            autoEl: {
                
                unselectable: 'on'
            },
            listeners: {
                render: me.onTitleRender,
                scope: me
            }
        });
        me.layout = (me.orientation == 'vertical') ? {
            type : 'vbox',
            align: 'center',
            alignRoundingMethod: 'ceil'
        } : {
            type : 'hbox',
            align: 'middle',
            alignRoundingMethod: 'floor'
        };

        
        Ext.Array.push(items, me.tools);
        
        
        me.tools.length = 0;
        me.callParent();

        if (items.length < titlePosition) {
            titlePosition = items.length;
        }
        me.titlePosition = titlePosition;

        
        me.insert(titlePosition, me.titleCmp);

        me.on({
            dblclick: me.onDblClick,
            click: me.onClick,
            element: 'el',
            scope: me
        });
    },

    
    setTitlePosition: function(index) {
        var me = this;
    
        me.titlePosition = index = Math.min(index, me.items.length - 1);
        me.insert(index, me.titleCmp);
    },

    initIconCmp: function() {
        var me = this,
            cls = [me.headerCls + '-icon', me.baseCls + '-icon', me.iconCls],
            cfg;
       
        if (me.glyph) {
            cls.push(me.baseCls + '-glyph');
        }

        cfg = {
            focusable: false,
            src: Ext.BLANK_IMAGE_URL,
            cls: cls,
            baseCls: me.baseCls + '-icon',
            id: me.id + '-iconEl',
            iconCls: me.iconCls,
            glyph:  me.glyph
        };

        if (!Ext.isEmpty(me.icon)) {
            delete cfg.iconCls;
            cfg.src = me.icon;
        }

        me.iconCmp = new Ext.Img(cfg);
    },

    beforeRender: function() {
        this.protoEl.unselectable();
        this.callParent();
    },

    afterLayout: function() {
        var me = this,
            frameBR, frameTR, frameTL, xPos;

        if (me.orientation === 'vertical') {
            me.adjustTitlePosition();
            frameTR = me.frameTR;
            if (frameTR) {
                
                
                
                frameBR = me.frameBR;
                frameTL = me.frameTL;
                xPos = (me.getWidth() - frameTR.getPadding('r') -
                    ((frameTL) ? frameTL.getPadding('l') : me.el.getBorderWidth('l'))) + 'px';
                frameBR.setStyle('background-position-x', xPos);
                frameTR.setStyle('background-position-x', xPos);
            }
            if (Ext.isIE7 && Ext.isStrict && me.frame) {
                
                
                me.el.repaint();
            }
        }
    },

    beforeLayout: function () {
        this.callParent();
        this.syncBeforeAfterTitleClasses();
    },

    adjustTitlePosition: function() {
        var titleCmp = this.titleCmp,
            titleEl;

        if (!Ext.isIE9m && titleCmp) { 
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            titleEl = titleCmp.el;
            titleEl.setStyle('left', titleEl.getWidth() + 'px');
        }
    },

    onTitleRender: function() {
        if (this.orientation === 'vertical') {
            this.titleCmp.el.setVertical(90);
        }
    },

    
    addUIClsToElement: function(cls) {
        var me = this,
            result = me.callParent(arguments),
            classes = [me.baseCls + '-body-' + cls, me.baseCls + '-body-' + me.ui + '-' + cls],
            array, i;

        if (me.bodyCls) {
            array = me.bodyCls.split(' ');

            for (i = 0; i < classes.length; i++) {
                if (!Ext.Array.contains(array, classes[i])) {
                    array.push(classes[i]);
                }
            }

            me.bodyCls = array.join(' ');
        } else {
            me.bodyCls = classes.join(' ');
        }

        return result;
    },

    
    removeUIClsFromElement: function(cls) {
        var me = this,
            result = me.callParent(arguments),
            classes = [me.baseCls + '-body-' + cls, me.baseCls + '-body-' + me.ui + '-' + cls],
            array, i;

        if (me.bodyCls) {
            array = me.bodyCls.split(' ');

            for (i = 0; i < classes.length; i++) {
                Ext.Array.remove(array, classes[i]);
            }

            me.bodyCls = array.join(' ');
        }

        return result;
    },

    
    addUIToElement: function() {
        var me = this,
            array, cls;

        me.callParent(arguments);

        cls = me.baseCls + '-body-' + me.ui;
        if (me.rendered) {
            if (me.bodyCls) {
                me.body.addCls(me.bodyCls);
            } else {
                me.body.addCls(cls);
            }
        } else {
            if (me.bodyCls) {
                array = me.bodyCls.split(' ');

                if (!Ext.Array.contains(array, cls)) {
                    array.push(cls);
                }

                me.bodyCls = array.join(' ');
            } else {
                me.bodyCls = cls;
            }
        }

        if (me.titleCmp && me.titleCmp.rendered) {
            me.titleCmp.addCls(me.baseCls + '-text-container-' + me.ui);
        }
    },

    
    removeUIFromElement: function() {
        var me = this,
            array, cls;

        me.callParent(arguments);

        cls = me.baseCls + '-body-' + me.ui;
        if (me.rendered) {
            if (me.bodyCls) {
                me.body.removeCls(me.bodyCls);
            } else {
                me.body.removeCls(cls);
            }
        } else {
            if (me.bodyCls) {
                array = me.bodyCls.split(' ');
                Ext.Array.remove(array, cls);
                me.bodyCls = array.join(' ');
            } else {
                me.bodyCls = cls;
            }
        }

        if (me.titleCmp && me.titleCmp.rendered) {
            me.titleCmp.removeCls(me.baseCls + '-text-container-' + me.ui);
        }
    },

    onClick: function(e) {
        this.fireClickEvent('click', e);
    },

    onDblClick: function(e){
        this.fireClickEvent('dblclick', e);
    },

    fireClickEvent: function(type, e){
        var toolCls = '.' + Ext.panel.Tool.prototype.baseCls;
        if (!e.getTarget(toolCls)) {
            this.fireEvent(type, this, e);
        }
    },

    getFocusEl: function() {
        return this.el;
    },

    getTargetEl: function() {
        return this.body || this.frameBody || this.el;
    },

    applyTargetCls: function(targetCls) {
        this.bodyTargetCls = targetCls;
    },

    
    setTitle: function(title) {
        var me = this,
            titleCmp = me.titleCmp;

        me.title = title;
        if (titleCmp.rendered) {
            titleCmp.textEl.update(me.title || '&#160;');
            titleCmp.updateLayout();
        } else {
            me.titleCmp.on({
                render: function() {
                    me.setTitle(title);
                },
                single: true
            });
        }
    },

    
    getMinWidth: function() {
        var me = this,
            textEl = me.titleCmp.textEl.dom,
            result,
            tools = me.tools,
            l, i;

        
        textEl.style.display = 'inline';
        result = textEl.offsetWidth;
        textEl.style.display = '';

        
        if (tools && (l = tools.length)) {
            for (i = 0; i < l; i++) {
                if (tools[i].el) {
                    result += tools[i].el.dom.offsetWidth;
                }
            }
        }

        
        if (me.iconCmp) {
            result += me.iconCmp.el.dom.offsetWidth;
        }

        
        return result + 10;
    },

    
    setIconCls: function(cls) {
        var me = this,
            isEmpty = !cls || !cls.length,
            iconCmp = me.iconCmp;

        me.iconCls = cls;
        if (!me.iconCmp && !isEmpty) {
            me.initIconCmp();
            me.insert(0, me.iconCmp);
        } else if (iconCmp) {
            if (isEmpty) {
                me.iconCmp.destroy();
                delete me.iconCmp;
            } else {
                iconCmp.removeCls(iconCmp.iconCls);
                iconCmp.addCls(cls);
                iconCmp.iconCls = cls;
            }
        }
    },

    
    setIcon: function(icon) {
        var me = this,
            isEmpty = !icon || !icon.length,
            iconCmp = me.iconCmp;

        me.icon = icon;
        if (!me.iconCmp && !isEmpty) {
            me.initIconCmp();
            me.insert(0, me.iconCmp);
        } else if (iconCmp) {
            if (isEmpty) {
                me.iconCmp.destroy();
                delete me.iconCmp;
            } else {
                iconCmp.setSrc(me.icon);
            }
        }
    },

     
    setGlyph: function(glyph) {
        var me = this,
            iconCmp = me.iconCmp;
        
        if (!me.iconCmp) {
            me.initIconCmp();
            me.insert(0, me.iconCmp);
        } else if (iconCmp) {
            if (glyph) {
                me.iconCmp.setGlyph(glyph);
            } else {
                me.iconCmp.destroy();
                delete me.iconCmp;
            }
        }
    },

    
    getTools: function(){
        return this.tools.slice();
    },

    
    addTool: function(tool) {
        
        
        this.add(Ext.ComponentManager.create(tool, 'tool'));
    },

    syncBeforeAfterTitleClasses: function(force) {
        var me = this,
            items = me.items,
            childItems = items.items,
            titlePosition = me.titlePosition,
            itemCount = childItems.length,
            itemGeneration = items.generation,
            syncGen = me.syncBeforeAfterGen,
            afterCls, beforeCls, i, item;

        if (!force && (syncGen === itemGeneration)) {
            return;
        }
        me.syncBeforeAfterGen = itemGeneration;

        for (i = 0; i < itemCount; ++i) {
            item = childItems[i];

            afterCls  = item.afterTitleCls  || (item.afterTitleCls  = item.baseCls + '-after-title')
            beforeCls = item.beforeTitleCls || (item.beforeTitleCls = item.baseCls + '-before-title')

            if (!me.title || i < titlePosition) {
                if (syncGen) {
                    item.removeCls(afterCls);
                } 
                item.addCls(beforeCls);
            } else if (i > titlePosition) {
                if (syncGen) {
                    item.removeCls(beforeCls);
                }
                item.addCls(afterCls);
            }
        }
    },

    
    onAdd: function(component, index) {
        var tools = this.tools;
        this.callParent(arguments);
        if (component.isTool) {
            tools.push(component);
            tools[component.type] = component;
        }
    },

    
    initRenderData: function() {
        return Ext.applyIf(this.callParent(), {
            bodyCls: this.bodyCls,
            bodyTargetCls: this.bodyTargetCls,
            headerCls: this.headerCls
        });
    },

    getDockName: function() {
        return this.dock;
    },
    
    getFramingInfoCls: function(){
        var me = this,
            cls = me.callParent(),
            owner = me.ownerCt;
            
        if (!me.expanding && owner && (owner.collapsed || me.isCollapsedExpander)) {
            cls += '-' + owner.collapsedCls; 
        }
        return cls + '-' + me.dock;
    }
});





Ext.define('Ext.dd.DragDrop', {
                                         

    
    constructor: function(id, sGroup, config) {
        if(id) {
            this.init(id, sGroup, config);
        }
    },

    

    
    id: null,

    
    config: null,

    
    dragElId: null,

    
    handleElId: null,

    
    invalidHandleTypes: null,

    
    invalidHandleIds: null,

    
    invalidHandleClasses: null,

    
    startPageX: 0,

    
    startPageY: 0,

    
    groups: null,

    
    locked: false,

    
    lock: function() {
        this.locked = true;
    },

    
    moveOnly: false,

    
    unlock: function() {
        this.locked = false;
    },

    
    isTarget: true,

    
    padding: null,

    
    _domRef: null,

    
    __ygDragDrop: true,

    
    constrainX: false,

    
    constrainY: false,

    
    minX: 0,

    
    maxX: 0,

    
    minY: 0,

    
    maxY: 0,

    
    maintainOffset: false,

    
    xTicks: null,

    
    yTicks: null,

    
    primaryButtonOnly: true,

    
    available: false,

    
    hasOuterHandles: false,

    
    b4StartDrag: function(x, y) { },

    
    startDrag: function(x, y) {  },

    
    b4Drag: function(e) { },

    
    onDrag: function(e) {  },

    
    onDragEnter: function(e, id) {  },

    
    b4DragOver: function(e) { },

    
    onDragOver: function(e, id) {  },

    
    b4DragOut: function(e) { },

    
    onDragOut: function(e, id) {  },

    
    b4DragDrop: function(e) { },

    
    onDragDrop: function(e, id) {  },

    
    onInvalidDrop: function(e) {  },

    
    b4EndDrag: function(e) { },

    
    endDrag: function(e) {  },

    
    b4MouseDown: function(e) {  },

    
    onMouseDown: function(e) {  },

    
    onMouseUp: function(e) {  },

    
    onAvailable: function () {
    },

    
    defaultPadding: {
        left: 0,
        right: 0,
        top: 0,
        bottom: 0
    },

    
    constrainTo : function(constrainTo, pad, inContent){
        if(Ext.isNumber(pad)){
            pad = {left: pad, right:pad, top:pad, bottom:pad};
        }
        pad = pad || this.defaultPadding;
        var b = Ext.get(this.getEl()).getBox(),
            ce = Ext.get(constrainTo),
            s = ce.getScroll(),
            c,
            cd = ce.dom,
            xy,
            topSpace,
            leftSpace;
        if(cd == document.body){
            c = { x: s.left, y: s.top, width: Ext.Element.getViewWidth(), height: Ext.Element.getViewHeight()};
        }else{
            xy = ce.getXY();
            c = {x : xy[0], y: xy[1], width: cd.clientWidth, height: cd.clientHeight};
        }

        topSpace = b.y - c.y;
        leftSpace = b.x - c.x;

        this.resetConstraints();
        this.setXConstraint(leftSpace - (pad.left||0), 
                c.width - leftSpace - b.width - (pad.right||0), 
        this.xTickSize
        );
        this.setYConstraint(topSpace - (pad.top||0), 
                c.height - topSpace - b.height - (pad.bottom||0), 
        this.yTickSize
        );
    },

    
    getEl: function() {
        if (!this._domRef) {
            this._domRef = Ext.getDom(this.id);
        }

        return this._domRef;
    },

    
    getDragEl: function() {
        return Ext.getDom(this.dragElId);
    },

    
    init: function(id, sGroup, config) {
        this.initTarget(id, sGroup, config);
        Ext.EventManager.on(this.id, "mousedown", this.handleMouseDown, this);
        
    },

    
    initTarget: function(id, sGroup, config) {
        
        this.config = config || {};

        
        this.DDMInstance = Ext.dd.DragDropManager;
        
        this.groups = {};

        
        
        if (typeof id !== "string") {
            id = Ext.id(id);
        }

        
        this.id = id;

        
        this.addToGroup((sGroup) ? sGroup : "default");

        
        
        this.handleElId = id;

        
        this.setDragElId(id);

        
        this.invalidHandleTypes = { A: "A" };
        this.invalidHandleIds = {};
        this.invalidHandleClasses = [];

        this.applyConfig();

        this.handleOnAvailable();
    },

    
    applyConfig: function() {

        
        
        this.padding           = this.config.padding || [0, 0, 0, 0];
        this.isTarget          = (this.config.isTarget !== false);
        this.maintainOffset    = (this.config.maintainOffset);
        this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);

    },

    
    handleOnAvailable: function() {
        this.available = true;
        this.resetConstraints();
        this.onAvailable();
    },

    
    setPadding: function(iTop, iRight, iBot, iLeft) {
        
        if (!iRight && 0 !== iRight) {
            this.padding = [iTop, iTop, iTop, iTop];
        } else if (!iBot && 0 !== iBot) {
            this.padding = [iTop, iRight, iTop, iRight];
        } else {
            this.padding = [iTop, iRight, iBot, iLeft];
        }
    },

    
    setInitPosition: function(diffX, diffY) {
        var el = this.getEl(),
            dx, dy, p;

        if (!this.DDMInstance.verifyEl(el)) {
            return;
        }

        dx = diffX || 0;
        dy = diffY || 0;

        p = Ext.Element.getXY( el );

        this.initPageX = p[0] - dx;
        this.initPageY = p[1] - dy;

        this.lastPageX = p[0];
        this.lastPageY = p[1];

        this.setStartPosition(p);
    },

    
    setStartPosition: function(pos) {
        var p = pos || Ext.Element.getXY( this.getEl() );
        this.deltaSetXY = null;

        this.startPageX = p[0];
        this.startPageY = p[1];
    },

    
    addToGroup: function(sGroup) {
        this.groups[sGroup] = true;
        this.DDMInstance.regDragDrop(this, sGroup);
    },

    
    removeFromGroup: function(sGroup) {
        if (this.groups[sGroup]) {
            delete this.groups[sGroup];
        }

        this.DDMInstance.removeDDFromGroup(this, sGroup);
    },

    
    setDragElId: function(id) {
        this.dragElId = id;
    },

    
    setHandleElId: function(id) {
        if (typeof id !== "string") {
            id = Ext.id(id);
        }
        this.handleElId = id;
        this.DDMInstance.regHandle(this.id, id);
    },

    
    setOuterHandleElId: function(id) {
        if (typeof id !== "string") {
            id = Ext.id(id);
        }
        Ext.EventManager.on(id, "mousedown", this.handleMouseDown, this);
        this.setHandleElId(id);

        this.hasOuterHandles = true;
    },

    
    unreg: function() {
        var me = this;
        
        Ext.EventManager.un(me.id, "mousedown", me.handleMouseDown, me);
        me._domRef = null;
        me.DDMInstance._remove(me, me.autoGroup);
    },

    
    destroy : function(){
        this.unreg();
    },

    
    isLocked: function() {
        return (this.DDMInstance.isLocked() || this.locked);
    },

    
    handleMouseDown: function(e, oDD){
        var me = this;

        if ((me.primaryButtonOnly && e.button != 0) || me.isLocked()) {
            return;
        }

        me.DDMInstance.refreshCache(me.groups);

        if (me.hasOuterHandles || me.DDMInstance.isOverTarget(e.getPoint(), me))  {
            if (me.clickValidator(e)) {
                
                me.setStartPosition();
                me.b4MouseDown(e);
                me.onMouseDown(e);

                me.DDMInstance.handleMouseDown(e, me);

                me.DDMInstance.stopEvent(e);
            }
        }
    },

    clickValidator: function(e) {
        var target = e.getTarget();
        return ( this.isValidHandleChild(target) &&
                    (this.id == this.handleElId ||
                        this.DDMInstance.handleWasClicked(target, this.id)) );
    },

    
    addInvalidHandleType: function(tagName) {
        var type = tagName.toUpperCase();
        this.invalidHandleTypes[type] = type;
    },

    
    addInvalidHandleId: function(id) {
        if (typeof id !== "string") {
            id = Ext.id(id);
        }
        this.invalidHandleIds[id] = id;
    },

    
    addInvalidHandleClass: function(cssClass) {
        this.invalidHandleClasses.push(cssClass);
    },

    
    removeInvalidHandleType: function(tagName) {
        var type = tagName.toUpperCase();
        
        delete this.invalidHandleTypes[type];
    },

    
    removeInvalidHandleId: function(id) {
        if (typeof id !== "string") {
            id = Ext.id(id);
        }
        delete this.invalidHandleIds[id];
    },

    
    removeInvalidHandleClass: function(cssClass) {
        for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
            if (this.invalidHandleClasses[i] == cssClass) {
                delete this.invalidHandleClasses[i];
            }
        }
    },

    
    isValidHandleChild: function(node) {

        var valid = true,
            nodeName,
            i, len;
        
        try {
            nodeName = node.nodeName.toUpperCase();
        } catch(e) {
            nodeName = node.nodeName;
        }
        valid = valid && !this.invalidHandleTypes[nodeName];
        valid = valid && !this.invalidHandleIds[node.id];

        for (i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
            valid = !Ext.fly(node).hasCls(this.invalidHandleClasses[i]);
        }


        return valid;

    },

    
    setXTicks: function(iStartX, iTickSize) {
        this.xTicks = [];
        this.xTickSize = iTickSize;

        var tickMap = {},
            i;

        for (i = this.initPageX; i >= this.minX; i = i - iTickSize) {
            if (!tickMap[i]) {
                this.xTicks[this.xTicks.length] = i;
                tickMap[i] = true;
            }
        }

        for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
            if (!tickMap[i]) {
                this.xTicks[this.xTicks.length] = i;
                tickMap[i] = true;
            }
        }

        Ext.Array.sort(this.xTicks, this.DDMInstance.numericSort);
    },

    
    setYTicks: function(iStartY, iTickSize) {
        this.yTicks = [];
        this.yTickSize = iTickSize;

        var tickMap = {},
            i;

        for (i = this.initPageY; i >= this.minY; i = i - iTickSize) {
            if (!tickMap[i]) {
                this.yTicks[this.yTicks.length] = i;
                tickMap[i] = true;
            }
        }

        for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
            if (!tickMap[i]) {
                this.yTicks[this.yTicks.length] = i;
                tickMap[i] = true;
            }
        }

        Ext.Array.sort(this.yTicks, this.DDMInstance.numericSort);
    },

    
    setXConstraint: function(iLeft, iRight, iTickSize) {
        this.leftConstraint = iLeft;
        this.rightConstraint = iRight;

        this.minX = this.initPageX - iLeft;
        this.maxX = this.initPageX + iRight;
        if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }

        this.constrainX = true;
    },

    
    clearConstraints: function() {
        this.constrainX = false;
        this.constrainY = false;
        this.clearTicks();
    },

    
    clearTicks: function() {
        this.xTicks = null;
        this.yTicks = null;
        this.xTickSize = 0;
        this.yTickSize = 0;
    },

    
    setYConstraint: function(iUp, iDown, iTickSize) {
        this.topConstraint = iUp;
        this.bottomConstraint = iDown;

        this.minY = this.initPageY - iUp;
        this.maxY = this.initPageY + iDown;
        if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }

        this.constrainY = true;

    },

    
    resetConstraints: function() {
        
        if (this.initPageX || this.initPageX === 0) {
            
            var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0,
                dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;

            this.setInitPosition(dx, dy);

        
        } else {
            this.setInitPosition();
        }

        if (this.constrainX) {
            this.setXConstraint( this.leftConstraint,
                                 this.rightConstraint,
                                 this.xTickSize        );
        }

        if (this.constrainY) {
            this.setYConstraint( this.topConstraint,
                                 this.bottomConstraint,
                                 this.yTickSize         );
        }
    },

    
    getTick: function(val, tickArray) {
        if (!tickArray) {
            
            
            return val;
        } else if (tickArray[0] >= val) {
            
            
            return tickArray[0];
        } else {
            var i, len, next, diff1, diff2;
            for (i=0, len=tickArray.length; i<len; ++i) {
                next = i + 1;
                if (tickArray[next] && tickArray[next] >= val) {
                    diff1 = val - tickArray[i];
                    diff2 = tickArray[next] - val;
                    return (diff2 > diff1) ? tickArray[i] : tickArray[next];
                }
            }

            
            
            return tickArray[tickArray.length - 1];
        }
    },

    
    toString: function() {
        return ("DragDrop " + this.id);
    }

});





Ext.define('Ext.dd.DD', {
    extend:  Ext.dd.DragDrop ,
                                         

    
    constructor: function(id, sGroup, config) {
        if (id) {
            this.init(id, sGroup, config);
        }
    },

    
    scroll: true,

    
    autoOffset: function(iPageX, iPageY) {
        var x = iPageX - this.startPageX,
            y = iPageY - this.startPageY;
        this.setDelta(x, y);
    },

    
    setDelta: function(iDeltaX, iDeltaY) {
        this.deltaX = iDeltaX;
        this.deltaY = iDeltaY;
    },

    
    setDragElPos: function(iPageX, iPageY) {
        
        

        var el = this.getDragEl();
        this.alignElWithMouse(el, iPageX, iPageY);
    },

    
    alignElWithMouse: function(el, iPageX, iPageY) {
        var oCoord = this.getTargetCoord(iPageX, iPageY),
            fly = el.dom ? el : Ext.fly(el, '_dd'),
            elSize = fly.getSize(),
            EL = Ext.Element,
            vpSize,
            aCoord,
            newLeft,
            newTop;

        if (!this.deltaSetXY) {
            vpSize = this.cachedViewportSize = { width: EL.getDocumentWidth(), height: EL.getDocumentHeight() };
            aCoord = [
                Math.max(0, Math.min(oCoord.x, vpSize.width - elSize.width)),
                Math.max(0, Math.min(oCoord.y, vpSize.height - elSize.height))
            ];
            fly.setXY(aCoord);
            newLeft = this.getLocalX(fly);
            newTop  = fly.getLocalY();
            this.deltaSetXY = [newLeft - oCoord.x, newTop - oCoord.y];
        } else {
            vpSize = this.cachedViewportSize;
            this.setLocalXY(
                fly,
                Math.max(0, Math.min(oCoord.x + this.deltaSetXY[0], vpSize.width - elSize.width)),
                Math.max(0, Math.min(oCoord.y + this.deltaSetXY[1], vpSize.height - elSize.height))
            );
        }

        this.cachePosition(oCoord.x, oCoord.y);
        this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
        return oCoord;
    },

    
    cachePosition: function(iPageX, iPageY) {
        if (iPageX) {
            this.lastPageX = iPageX;
            this.lastPageY = iPageY;
        } else {
            var aCoord = Ext.Element.getXY(this.getEl());
            this.lastPageX = aCoord[0];
            this.lastPageY = aCoord[1];
        }
    },

    
    autoScroll: function(x, y, h, w) {

        if (this.scroll) {
            
            var clientH = Ext.Element.getViewHeight(),
                
                clientW = Ext.Element.getViewWidth(),
                
                st = this.DDMInstance.getScrollTop(),
                
                sl = this.DDMInstance.getScrollLeft(),
                
                bot = h + y,
                
                right = w + x,
                
                
                
                toBot = (clientH + st - y - this.deltaY),
                
                toRight = (clientW + sl - x - this.deltaX),
                
                
                thresh = 40,
                
                
                
                scrAmt = (document.all) ? 80 : 30;

            
            
            if ( bot > clientH && toBot < thresh ) {
                window.scrollTo(sl, st + scrAmt);
            }

            
            
            if ( y < st && st > 0 && y - st < thresh ) {
                window.scrollTo(sl, st - scrAmt);
            }

            
            
            if ( right > clientW && toRight < thresh ) {
                window.scrollTo(sl + scrAmt, st);
            }

            
            
            if ( x < sl && sl > 0 && x - sl < thresh ) {
                window.scrollTo(sl - scrAmt, st);
            }
        }
    },

    
    getTargetCoord: function(iPageX, iPageY) {
        var x = iPageX - this.deltaX,
            y = iPageY - this.deltaY;

        if (this.constrainX) {
            if (x < this.minX) {
                x = this.minX;
            }
            if (x > this.maxX) {
                x = this.maxX;
            }
        }

        if (this.constrainY) {
            if (y < this.minY) {
                y = this.minY;
            }
            if (y > this.maxY) {
                y = this.maxY;
            }
        }

        x = this.getTick(x, this.xTicks);
        y = this.getTick(y, this.yTicks);


        return {x: x, y: y};
    },

    
    applyConfig: function() {
        this.callParent();
        this.scroll = (this.config.scroll !== false);
    },

    
    b4MouseDown: function(e) {
        
        this.autoOffset(e.getPageX(), e.getPageY());
    },

    
    b4Drag: function(e) {
        this.setDragElPos(e.getPageX(), e.getPageY());
    },

    toString: function() {
        return ("DD " + this.id);
    },
    
    getLocalX: function(el) {
        return el.getLocalX();
    },

    setLocalXY: function(el, x, y) {
        el.setLocalXY(x, y);
    }

    
    
    
    

});




Ext.define('Ext.dd.DDProxy', {
    extend:  Ext.dd.DD ,

    statics: {
        
        dragElId: "ygddfdiv"
    },

    
    constructor: function(id, sGroup, config) {
        if (id) {
            this.init(id, sGroup, config);
            this.initFrame();
        }
    },

    
    resizeFrame: true,

    
    centerFrame: false,

    
    createFrame: function() {
        var self = this,
            body = document.body,
            div,
            s;

        if (!body || !body.firstChild) {
            setTimeout( function() { self.createFrame(); }, 50 );
            return;
        }

        div = this.getDragEl();

        if (!div) {
            div    = document.createElement("div");
            div.id = this.dragElId;
            div.setAttribute('role', 'presentation');
            s  = div.style;

            s.position   = "absolute";
            s.visibility = "hidden";
            s.cursor     = "move";
            s.border     = "2px solid #aaa";
            s.zIndex     = 999;

            
            
            
            body.insertBefore(div, body.firstChild);
        }
    },

    
    initFrame: function() {
        this.createFrame();
    },

    applyConfig: function() {
        this.callParent();

        this.resizeFrame = (this.config.resizeFrame !== false);
        this.centerFrame = (this.config.centerFrame);
        this.setDragElId(this.config.dragElId || Ext.dd.DDProxy.dragElId);
    },

    
    showFrame: function(iPageX, iPageY) {
        var el = this.getEl(),
            dragEl = this.getDragEl(),
            s = dragEl.style;

        this._resizeProxy();

        if (this.centerFrame) {
            this.setDelta( Math.round(parseInt(s.width,  10)/2),
                           Math.round(parseInt(s.height, 10)/2) );
        }

        this.setDragElPos(iPageX, iPageY);

        Ext.fly(dragEl).show();
    },

    
    _resizeProxy: function() {
        if (this.resizeFrame) {
            var el = this.getEl();
            Ext.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
        }
    },

    
    b4MouseDown: function(e) {
        var x = e.getPageX(),
            y = e.getPageY();
        this.autoOffset(x, y);
        this.setDragElPos(x, y);
    },

    
    b4StartDrag: function(x, y) {
        
        this.showFrame(x, y);
    },

    
    b4EndDrag: function(e) {
        Ext.fly(this.getDragEl()).hide();
    },

    
    
    
    endDrag: function(e) {

        var lel = this.getEl(),
            del = this.getDragEl();

        
        del.style.visibility = "";

        this.beforeMove();
        
        
        lel.style.visibility = "hidden";
        Ext.dd.DDM.moveToEl(lel, del);
        del.style.visibility = "hidden";
        lel.style.visibility = "";

        this.afterDrag();
    },

    beforeMove : function(){

    },

    afterDrag : function(){

    },

    toString: function() {
        return ("DDProxy " + this.id);
    }

});


Ext.define('Ext.dd.StatusProxy', {
    extend:  Ext.Component ,
    animRepair: false,

    childEls: [
        'ghost'
    ],

    renderTpl: [
        '<div class="' + Ext.baseCSSPrefix + 'dd-drop-icon" role="presentation"></div>' +
        '<div id="{id}-ghost" class="' + Ext.baseCSSPrefix + 'dd-drag-ghost" role="presentation"></div>'
    ],
    
    repairCls: Ext.baseCSSPrefix + 'dd-drag-repair',
    
    ariaRole: 'presentation',

    
    constructor: function(config) {
        var me = this;

        config = config || {};

        Ext.apply(me, {
            hideMode: 'visibility',
            hidden: true,
            floating: true,
            id: me.id || Ext.id(),
            cls: Ext.baseCSSPrefix + 'dd-drag-proxy ' + this.dropNotAllowed,
            shadow: config.shadow || false,
            renderTo: Ext.getDetachedBody()
        });
        me.callParent(arguments);
        this.dropStatus = this.dropNotAllowed;
    },

    
    dropAllowed : Ext.baseCSSPrefix + 'dd-drop-ok',

    
    dropNotAllowed : Ext.baseCSSPrefix + 'dd-drop-nodrop',

    
    setStatus : function(cssClass){
        cssClass = cssClass || this.dropNotAllowed;
        if (this.dropStatus != cssClass) {
            this.el.replaceCls(this.dropStatus, cssClass);
            this.dropStatus = cssClass;
        }
    },

    
    reset : function(clearGhost){
        var me = this,
            clsPrefix = Ext.baseCSSPrefix + 'dd-drag-proxy ';

        me.el.replaceCls(clsPrefix + me.dropAllowed, clsPrefix + me.dropNotAllowed);
        me.dropStatus = me.dropNotAllowed;
        if (clearGhost) {
            me.ghost.update('');
        }
    },

    
    update : function(html){
        if (typeof html == "string") {
            this.ghost.update(html);
        } else {
            this.ghost.update("");
            html.style.margin = "0";
            this.ghost.dom.appendChild(html);
        }
        var el = this.ghost.dom.firstChild;
        if (el) {
            Ext.fly(el).setStyle('float', 'none');
        }
    },

    
    getGhost : function(){
        return this.ghost;
    },

    
    hide : function(clear) {
        this.callParent();
        if (clear) {
            this.reset(true);
        }
    },

    
    stop : function(){
        if (this.anim && this.anim.isAnimated && this.anim.isAnimated()) {
            this.anim.stop();
        }
    },

    
    sync : function(){
        this.el.sync();
    },

    
    repair : function(xy, callback, scope) {
        var me = this;

        me.callback = callback;
        me.scope = scope;
        if (xy && me.animRepair !== false) {
            me.el.addCls(me.repairCls);
            me.el.hideUnders(true);
            me.anim = me.el.animate({
                duration: me.repairDuration || 500,
                easing: 'ease-out',
                to: {
                    x: xy[0],
                    y: xy[1]
                },
                stopAnimation: true,
                callback: me.afterRepair,
                scope: me
            });
        } else {
            me.afterRepair();
        }
    },

    
    afterRepair : function() {
        var me = this;
    
        me.hide(true);
        me.el.removeCls(me.repairCls);
        if (typeof me.callback == "function") {
            me.callback.call(me.scope || me);
        }
        delete me.callback;
        delete me.scope;
    }
});


Ext.define('Ext.dd.DragSource', {
    extend:  Ext.dd.DDProxy ,
               
                             
                                
      

    

    

    
    dropAllowed : Ext.baseCSSPrefix + 'dd-drop-ok',
    
    dropNotAllowed : Ext.baseCSSPrefix + 'dd-drop-nodrop',

    
    animRepair: true,

    
    repairHighlightColor: 'c3daf9',

    
    constructor: function(el, config) {
        this.el = Ext.get(el);
        if(!this.dragData){
            this.dragData = {};
        }

        Ext.apply(this, config);

        if(!this.proxy){
            this.proxy = new Ext.dd.StatusProxy({
                id: this.el.id + '-drag-status-proxy',
                animRepair: this.animRepair
            });
        }
        this.callParent([this.el.dom, this.ddGroup || this.group,
              {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true}]);

        this.dragging = false;
    },

    
    getDragData : function(e){
        return this.dragData;
    },

    
    onDragEnter : function(e, id){
        var target = Ext.dd.DragDropManager.getDDById(id),
            status;
        this.cachedTarget = target;
        if (this.beforeDragEnter(target, e, id) !== false) {
            if (target.isNotifyTarget) {
                status = target.notifyEnter(this, e, this.dragData);
                this.proxy.setStatus(status);
            } else {
                this.proxy.setStatus(this.dropAllowed);
            }

            if (this.afterDragEnter) {
                
                this.afterDragEnter(target, e, id);
            }
        }
    },

    
    beforeDragEnter: function(target, e, id) {
        return true;
    },

    
    onDragOver: function(e, id) {
        var target = this.cachedTarget || Ext.dd.DragDropManager.getDDById(id),
            status;
        if (this.beforeDragOver(target, e, id) !== false) {
            if(target.isNotifyTarget){
                status = target.notifyOver(this, e, this.dragData);
                this.proxy.setStatus(status);
            }

            if (this.afterDragOver) {
                
                this.afterDragOver(target, e, id);
            }
        }
    },

    
    beforeDragOver: function(target, e, id) {
        return true;
    },

    
    onDragOut: function(e, id) {
        var target = this.cachedTarget || Ext.dd.DragDropManager.getDDById(id);
        if (this.beforeDragOut(target, e, id) !== false) {
            if (target.isNotifyTarget) {
                target.notifyOut(this, e, this.dragData);
            }
            this.proxy.reset();
            if (this.afterDragOut) {
                
                this.afterDragOut(target, e, id);
            }
        }
        this.cachedTarget = null;
    },

    
    beforeDragOut: function(target, e, id){
        return true;
    },

    
    onDragDrop: function(e, id){
        var target = this.cachedTarget || Ext.dd.DragDropManager.getDDById(id);
        if (this.beforeDragDrop(target, e, id) !== false) {
            if (target.isNotifyTarget) {
                if (target.notifyDrop(this, e, this.dragData) !== false) { 
                    this.onValidDrop(target, e, id);
                } else {
                    this.onInvalidDrop(target, e, id);
                }
            } else {
                this.onValidDrop(target, e, id);
            }

            if (this.afterDragDrop) {
                
                this.afterDragDrop(target, e, id);
            }
        }
        delete this.cachedTarget;
    },

    
    beforeDragDrop: function(target, e, id){
        return true;
    },

    
    onValidDrop: function(target, e, id){
        this.hideProxy();
        if(this.afterValidDrop){
            
            this.afterValidDrop(target, e, id);
        }
    },

    
    getRepairXY: function(e, data){
        return this.el.getXY();
    },

    
    onInvalidDrop: function(target, e, id) {
        
        
        
        var me = this;
        
        if (!e) {
            e = target;
            target = null;
            id = e.getTarget().id;
        }
        if (me.beforeInvalidDrop(target, e, id) !== false) {
            if (me.cachedTarget) {
                if(me.cachedTarget.isNotifyTarget){
                    me.cachedTarget.notifyOut(me, e, me.dragData);
                }
                me.cacheTarget = null;
            }
            me.proxy.repair(me.getRepairXY(e, me.dragData), me.afterRepair, me);

            if (me.afterInvalidDrop) {
                
                me.afterInvalidDrop(e, id);
            }
        }
    },

    
    afterRepair: function() {
        var me = this;
        if (Ext.enableFx) {
            me.el.highlight(me.repairHighlightColor);
        }
        me.dragging = false;
    },

    
    beforeInvalidDrop: function(target, e, id) {
        return true;
    },

    
    handleMouseDown: function(e) {
        if (this.dragging) {
            return;
        }
        var data = this.getDragData(e);
        if (data && this.onBeforeDrag(data, e) !== false) {
            this.dragData = data;
            this.proxy.stop();
            this.callParent(arguments);
        }
    },

    
    onBeforeDrag: function(data, e){
        return true;
    },

    
    onStartDrag: Ext.emptyFn,

    alignElWithMouse: function() {
        this.proxy.ensureAttachedToBody(true);
        return this.callParent(arguments);
    },

    
    startDrag: function(x, y) {
        this.proxy.reset();
        this.proxy.hidden = false;
        this.dragging = true;
        this.proxy.update("");
        this.onInitDrag(x, y);
        this.proxy.show();
    },

    
    onInitDrag: function(x, y) {
        var clone = this.el.dom.cloneNode(true);
        clone.id = Ext.id(); 
        this.proxy.update(clone);
        this.onStartDrag(x, y);
        return true;
    },

    
    getProxy: function() {
        return this.proxy;
    },

    
    hideProxy: function() {
        this.proxy.hide();
        this.proxy.reset(true);
        this.dragging = false;
    },

    
    triggerCacheRefresh: function() {
        Ext.dd.DDM.refreshCache(this.groups);
    },

    
    b4EndDrag: function(e) {
    },

    
    endDrag : function(e){
        this.onEndDrag(this.dragData, e);
    },

    
    onEndDrag : function(data, e){
    },

    
    autoOffset : function(x, y) {
        this.setDelta(-12, -20);
    },

    destroy: function(){
        this.callParent();
        Ext.destroy(this.proxy);
    }
});


Ext.define('Ext.panel.Proxy', {

    alternateClassName: 'Ext.dd.PanelProxy',
    
    
    moveOnDrag: true,

    
    constructor: function(panel, config){
        var me = this;
        
        
        me.panel = panel;
        me.id = me.panel.id +'-ddproxy';
        Ext.apply(me, config);
    },

    
    insertProxy: true,

    
    setStatus: Ext.emptyFn,
    reset: Ext.emptyFn,
    update: Ext.emptyFn,
    stop: Ext.emptyFn,
    sync: Ext.emptyFn,

    
    getEl: function(){
        return this.ghost.el;
    },

    
    getGhost: function(){
        return this.ghost;
    },

    
    getProxy: function(){
        return this.proxy;
    },

    
    hide : function(){
        var me = this;
        
        if (me.ghost) {
            if (me.proxy) {
                me.proxy.remove();
                delete me.proxy;
            }

            
            me.panel.unghost(null, me.moveOnDrag);
            delete me.ghost;
        }
    },

    
    show: function(){
        var me = this,
            panelSize;
            
        if (!me.ghost) {
            panelSize = me.panel.getSize();
            me.panel.el.setVisibilityMode(Ext.Element.DISPLAY);
            me.ghost = me.panel.ghost();
            if (me.insertProxy) {
                
                
                me.proxy = me.panel.el.insertSibling({
                    role: 'presentation',
                    cls: Ext.baseCSSPrefix + 'panel-dd-spacer'
                });
                me.proxy.setSize(panelSize);
            }
        }
    },

    
    repair: function(xy, callback, scope) {
        this.hide();
        Ext.callback(callback, scope || this);
    },

    
    moveProxy : function(parentNode, before){
        if (this.proxy) {
            parentNode.insertBefore(this.proxy.dom, before);
        }
    }
});


Ext.define('Ext.panel.DD', {
    extend:  Ext.dd.DragSource ,
                                  

    constructor : function(panel, cfg){
        var me = this;
        
        me.panel = panel;
        me.dragData = {panel: panel};
        me.panelProxy = new Ext.panel.Proxy(panel, cfg);
        me.proxy = me.panelProxy.proxy;

        me.callParent([panel.el, cfg]);
        me.setupEl(panel);
    },
    
    setupEl: function(panel){
        var me = this,
            header = panel.header,
            el = panel.body;
            
        if (header) {
            me.setHandleElId(header.id);
            el = header.el;
        }
        if (el) {
            el.setStyle('cursor', 'move');
            me.scroll = false;
        } else {
            
            panel.on('boxready', me.setupEl, me, {single: true});
        }
    },

    showFrame: Ext.emptyFn,
    startDrag: Ext.emptyFn,
    
    b4StartDrag: function(x, y) {
        this.panelProxy.show();
    },
    
    b4MouseDown: function(e) {
        var x = e.getPageX(),
            y = e.getPageY();
            
        this.autoOffset(x, y);
    },
    
    onInitDrag : function(x, y){
        this.onStartDrag(x, y);
        return true;
    },
    
    createFrame : Ext.emptyFn,
    
    getDragEl : function(e){
        var ghost = this.panelProxy.ghost;
        if (ghost) {
            return ghost.el.dom;
        }
    },
    
    endDrag : function(e){
        this.panelProxy.hide();
        this.panel.saveState();
    },

    autoOffset : function(x, y) {
        x -= this.startPageX;
        y -= this.startPageY;
        this.setDelta(x, y);
    },
    
    
    
    onInvalidDrop: function(target, e, id) {
        var me = this;
        
        if (me.beforeInvalidDrop(target, e, id) !== false) {
            if (me.cachedTarget) {
                if(me.cachedTarget.isNotifyTarget){
                    me.cachedTarget.notifyOut(me, e, me.dragData);
                }
                me.cacheTarget = null;
            }

            if (me.afterInvalidDrop) {
                
                me.afterInvalidDrop(e, id);
            }
        }
    }
});


Ext.define('Ext.util.Memento', (function () {

    function captureOne (src, target, prop, prefix) {
        src[prefix ? prefix + prop : prop] = target[prop];
    }

    function removeOne (src, target, prop) {
        delete src[prop];
    }

    function restoreOne (src, target, prop, prefix) {
        var name = prefix ? prefix + prop : prop,
            value = src[name];

        if (value || src.hasOwnProperty(name)) {
            restoreValue(target, prop, value);
        }
    }

    function restoreValue (target, prop, value) {
        if (Ext.isDefined(value)) {
            target[prop] = value;
        } else {
            delete target[prop];
        }
    }

    function doMany (doOne, src, target, props, prefix) {
        if (src) {
            if (Ext.isArray(props)) {
                var p, pLen = props.length;
                for (p = 0; p < pLen; p++) {
                    doOne(src, target, props[p], prefix);
                }
            } else {
                doOne(src, target, props, prefix);
            }
        }
    }

    return {
        
        data: null,

        
        target: null,

        
        constructor: function (target, props) {
            if (target) {
                this.target = target;
                if (props) {
                    this.capture(props);
                }
            }
        },

        
        capture: function (props, target, prefix) {
            var me = this;
            doMany(captureOne, me.data || (me.data = {}), target || me.target, props, prefix);
        },

        
        remove: function (props) {
            doMany(removeOne, this.data, null, props);
        },

        
        restore: function (props, clear, target, prefix) {
            doMany(restoreOne, this.data, target || this.target, props, prefix);
            if (clear !== false) {
                this.remove(props);
            }
        },

        
        restoreAll: function (clear, target) {
            var me   = this,
                t    = target || this.target,
                data = me.data,
                prop;

            for (prop in data) {
                if (data.hasOwnProperty(prop)) {
                    restoreValue(t, prop, data[prop]);
                }
            }

            if (clear !== false) {
                delete me.data;
            }
        }
    };
}()));


Ext.define('Ext.panel.Panel', {
    extend:  Ext.panel.AbstractPanel ,
               
                           
                      
                          
                       
                        
                                    
                          
      
    alias: 'widget.panel',
    alternateClassName: 'Ext.Panel',

    
    collapsedCls: 'collapsed',

    
    animCollapse: Ext.enableFx,

    
    minButtonWidth: 75,

    
    collapsed: false,

    
    collapseFirst: true,

    
    hideCollapseTool: false,

    
    titleCollapse: undefined,

    

    

    
    floatable: true,

    

    
    collapsible: undefined,

    

    
    closable: false,

    
    closeAction: 'destroy',

    

    
    placeholderCollapseHideMode: Ext.Element.VISIBILITY,

    
     preventHeader: false,

    
    header: undefined,

    
    headerPosition: 'top',

    
    frame: false,

    
    frameHeader: true,

    

    

    

    
    manageHeight: true,

    
    
    

    

    

    
    constrain: false,

    
    constrainHeader: false,

    
    
    initComponent: function() {
        var me = this;

        me.addEvents(

            
            'beforeclose',
            
            
            'close',

            
            "beforeexpand",

            
            "beforecollapse",

            
            "expand",

            
            "collapse",

            
            'titlechange',

            
            'iconchange',
            
            
            'iconclschange',

            
            'glyphchange',

            
            'float',

            
            'unfloat'
        );

        if (me.collapsible) {
        
            me.addStateEvents(['expand', 'collapse']);
        }
        if (me.unstyled) {
            me.setUI('plain');
        }

        if (me.frame) {
            me.setUI(me.ui + '-framed');
        }

        
        me.bridgeToolbars();

        me.callParent();
        me.collapseDirection = me.collapseDirection || me.headerPosition || Ext.Component.DIRECTION_TOP;

        
        me.hiddenOnCollapse = new Ext.dom.CompositeElement();

    },

    beforeDestroy: function() {
        var me = this;
        Ext.destroy(
            me.placeholder,
            me.ghostPanel,
            me.dd
        );
        me.callParent();
    },

    getFocusEl: function() {
        return  this.el;
    },

    
    getHeader: function() {
        return this.header;
    },

    
    setTitle: function(newTitle) {
        var me = this,
            oldTitle = me.title,
            header = me.header,
            reExpander = me.reExpander,
            placeholder = me.placeholder;

        me.title = newTitle;

        if (header) {
            if (header.isHeader) {
                header.setTitle(newTitle);
            } else {
                header.title = newTitle;
            }
        } else if (me.rendered) {
            me.updateHeader();
        }

        if (reExpander) {
            reExpander.setTitle(newTitle);
        }

        if (placeholder && placeholder.setTitle) {
            placeholder.setTitle(newTitle);
        }

        me.fireEvent('titlechange', me, newTitle, oldTitle);
    },

    
    setIconCls: function(newIconCls) {
        var me = this,
            oldIconCls = me.iconCls,
            header = me.header,
            placeholder = me.placeholder;

        me.iconCls = newIconCls;

        if (header) {
            if (header.isHeader) {
                header.setIconCls(newIconCls);
            } else {
                header.iconCls = newIconCls;
            }
        } else {
            me.updateHeader();
        }

        if (placeholder && placeholder.setIconCls) {
            placeholder.setIconCls(newIconCls);
        }

        me.fireEvent('iconclschange', me, newIconCls, oldIconCls);
    },
    
    
    setIcon: function(newIcon) {
        var me = this,
            oldIcon = me.icon,
            header = me.header,
            placeholder = me.placeholder;

        me.icon = newIcon;

        if (header) {
            if (header.isHeader) {
                header.setIcon(newIcon);
            } else {
                header.icon = newIcon;
            }
        } else {
            me.updateHeader();
        }

        if (placeholder && placeholder.setIcon) {
            placeholder.setIcon(newIcon);
        }

        me.fireEvent('iconchange', me, newIcon, oldIcon);
    },

    
    setGlyph: function(newGlyph) {
        var me = this,
            oldGlyph = me.glyph,
            header = me.header,
            placeholder = me.placeholder;

        me.glyph = newGlyph;

        if (header) {
            if (header.isHeader) {
                header.setGlyph(newGlyph);
            } else {
                header.glyph = newGlyph;
            }
        } else {
            me.updateHeader();
        }

        if (placeholder && placeholder.setGlyph) {
            placeholder.setIcon(newGlyph);
        }

        me.fireEvent('glyphchange', me, newGlyph, oldGlyph);
    },

    bridgeToolbars: function() {
        var me = this,
            docked = [],
            minButtonWidth = me.minButtonWidth,
            fbar, fbarDefaults;

        function initToolbar (toolbar, pos, useButtonAlign) {
            if (Ext.isArray(toolbar)) {
                toolbar = {
                    xtype: 'toolbar',
                    items: toolbar
                };
            }
            else if (!toolbar.xtype) {
                toolbar.xtype = 'toolbar';
            }
            toolbar.dock = pos;
            if (pos == 'left' || pos == 'right') {
                toolbar.vertical = true;
            }

            
            if (useButtonAlign) {
                toolbar.layout = Ext.applyIf(toolbar.layout || {}, {
                    
                    pack: { left:'start', center:'center' }[me.buttonAlign] || 'end'
                });
            }
            return toolbar;
        }

        

        

        
        if (me.tbar) {
            docked.push(initToolbar(me.tbar, 'top'));
            me.tbar = null;
        }

        
        if (me.bbar) {
            docked.push(initToolbar(me.bbar, 'bottom'));
            me.bbar = null;
        }

        
        if (me.buttons) {
            me.fbar = me.buttons;
            me.buttons = null;
        }

        
        if (me.fbar) {
            fbar = initToolbar(me.fbar, 'bottom', true); 
            fbar.ui = 'footer';

            
            if (minButtonWidth) {
                fbarDefaults = fbar.defaults;
                fbar.defaults = function(config) {
                    var defaults = fbarDefaults || {},
                        
                        isButton = !config.xtype || config.isButton,
                        cls;
                       
                    
                    
                    if (!isButton) {
                        cls = Ext.ClassManager.getByAlias('widget.' + config.xtype);
                        if (cls) {
                            isButton = cls.prototype.isButton;
                        }
                    }
                    if (isButton && !('minWidth' in defaults)) {
                        defaults = Ext.apply({minWidth: minButtonWidth}, defaults);
                    }
                    return defaults;
                };
            }

            docked.push(fbar);
            me.fbar = null;
        }

        
        if (me.lbar) {
            docked.push(initToolbar(me.lbar, 'left'));
            me.lbar = null;
        }

        
        if (me.rbar) {
            docked.push(initToolbar(me.rbar, 'right'));
            me.rbar = null;
        }

        if (me.dockedItems) {
            if (!Ext.isArray(me.dockedItems)) {
                me.dockedItems = [me.dockedItems];
            }
            me.dockedItems = me.dockedItems.concat(docked);
        } else {
            me.dockedItems = docked;
        }
    },

    isPlaceHolderCollapse: function(){
        return this.collapseMode == 'placeholder';
    },

    onBoxReady: function(){
        this.callParent();
        if (this.collapsed) {
            this.setHiddenDocked();
        }    
    },

    beforeRender: function() {
        var me = this,
            wasCollapsed;

        me.callParent();

        
        
        me.initTools();

        
        if (!(me.preventHeader || (me.header === false))) {
            me.updateHeader();
        }

        
        if (me.collapsed) {
            if (me.isPlaceHolderCollapse()) {
                if (!me.hidden) {
                    me.setHiddenState(true);

                    
                    
                    
                    
                    me.preventCollapseFire = true;
                    me.placeholderCollapse();
                    delete me.preventCollapseFire;
                    wasCollapsed = me.collapsed;

                    
                    
                    
                    me.collapsed = false;
                }
            } else {
                me.beginCollapse();
                me.addClsWithUI(me.collapsedCls);
            }
        }

        
        if (wasCollapsed) {
            me.collapsed = wasCollapsed;
        }
    },

    
    initTools: function() {
        var me = this,
            tools = me.tools,
            i, tool;

        me.tools = [];
        for (i = tools && tools.length; i; ) {
            --i;
            me.tools[i] = tool = tools[i];
            tool.toolOwner = me;
        }

        
        
        if (me.collapsible && !(me.hideCollapseTool || me.header === false || me.preventHeader)) {
            me.collapseDirection = me.collapseDirection || me.headerPosition || 'top';
            me.collapseTool = me.expandTool = Ext.widget({
                xtype: 'tool',
                handler: me.toggleCollapse,
                scope: me
            });

            me.updateCollapseTool();
            
            if (me.collapseFirst) {
                me.tools.unshift(me.collapseTool);
            }
        }

        
        me.addTools();

        
        if (me.closable) {
            me.addClsWithUI('closable');
            me.addTool(Ext.widget({
                xtype : 'tool',
                type: 'close',
                handler: Ext.Function.bind(me.close, me, [])
            }));
        }

        
        if (me.collapseTool && !me.collapseFirst) {
            me.addTool(me.collapseTool);
        }
    },

    
    addTools: Ext.emptyFn,

    updateCollapseTool: function () {
        var me = this,
            collapseTool = me.collapseTool;
    
        if (collapseTool) {
            if (me.collapsed && !me.isPlaceHolderCollapse()) {
                collapseTool.setType('expand-' + me.getOppositeDirection(me.collapseDirection));
            } else {
                collapseTool.setType('collapse-' + me.collapseDirection);
            }
        }
    },

    
    close: function() {
        if (this.fireEvent('beforeclose', this) !== false) {
            this.doClose();
        }
    },

    
    doClose: function() {
        this.fireEvent('close', this);
        this[this.closeAction]();
    },

    
    updateHeader: function(force) {
        var me = this,
            header = me.header,
            title = me.title,
            tools = me.tools,
            icon = me.icon || me.iconCls,
            vertical = me.headerPosition === 'left' || me.headerPosition === 'right';

        if (Ext.isObject(header) || (header !== false && (force || (title || icon) || (tools && tools.length) || (me.collapsible && !me.titleCollapse)))) {
            if (header && header.isHeader) {
                header.show();
            } else {
                
                header = me.header = Ext.widget(Ext.apply({
                    xtype       : 'header',
                    title       : title,
                    titleAlign  : me.titleAlign,
                    orientation : vertical ? 'vertical' : 'horizontal',
                    dock        : me.headerPosition || 'top',
                    textCls     : me.headerTextCls,
                    iconCls     : me.iconCls,
                    icon        : me.icon,
                    glyph       : me.glyph,
                    baseCls     : me.baseCls + '-header',
                    tools       : tools,
                    ui          : me.ui,
                    id          : me.id + '_header',
                    overCls: me.headerOverCls,
                    indicateDrag: me.draggable,
                    frame       : (me.frame || me.alwaysFramed) && me.frameHeader,
                    ignoreParentFrame : me.frame || me.overlapHeader,
                    ignoreBorderManagement: me.frame || me.ignoreHeaderBorderManagement,
                    headerRole  : me.headerRole,
                    ownerCt     : me,
                    listeners   : me.collapsible && me.titleCollapse ? {
                        click: me.toggleCollapse,
                        scope: me
                    } : null
                }, me.header));
                
                
                
                me.addDocked(header, 0);
            }
        } else if (header) {
            header.hide();
        }
    },

    
    setUI: function(ui) {
        var me = this;

        me.callParent(arguments);

        if (me.header && me.header.rendered) {
            me.header.setUI(ui);
        }
    },

    
    getDefaultContentTarget: function() {
        return this.body;
    },

    getTargetEl: function() {
        var me = this;
        return me.body || me.protoBody || me.frameBody || me.el;
    },

    

    
    isVisible: function(deep){
        var me = this;
        if (me.collapsed && me.placeholder) {
            return me.placeholder.isVisible(deep);
        }
        return me.callParent(arguments);
    },

    
    onHide: function() {
        var me = this,
            dd = me.dd;
        
        if (me.floatedFromCollapse) {
            me.slideOutFloatedPanel(true);
        }

        if (me.draggable && dd) {
            
            dd.endDrag();
        }

        if (me.collapsed && me.placeholder) {
            me.placeholder.hide();
        } else {
            me.callParent(arguments);
        }
    },

    
    onShow: function() {
        var me = this;
        if (me.collapsed && me.isPlaceHolderCollapse()) {
            
            me.setHiddenState(true);
            me.placeholderCollapse();
        } else {
            me.callParent(arguments);
        }
    },

    onRemoved: function(destroying) {
        var me = this;

        
        
        
        if (me.placeholder && !destroying) {
            me.ownerCt.remove(me.placeholder, false);
        }

        me.callParent(arguments);
    },

    
    addTool: function(tools) {
        if (!Ext.isArray(tools)) {
            tools = [tools];
        }

        var me     = this,
            header = me.header,
            t,
            tLen   = tools.length,
            tool;

        for (t = 0; t < tLen; t++) {
            tool = tools[t];
            tool.toolOwner = me;
            
            if (header && header.isHeader) {
                header.addTool(tool);
            } else {
                
                
                me.tools.push(tool);
            }
        }

        me.updateHeader();
    },

    getOppositeDirection: function(d) {
        var c = Ext.Component;
        switch (d) {
            case c.DIRECTION_TOP:
                return c.DIRECTION_BOTTOM;
            case c.DIRECTION_RIGHT:
                return c.DIRECTION_LEFT;
            case c.DIRECTION_BOTTOM:
                return c.DIRECTION_TOP;
            case c.DIRECTION_LEFT:
                return c.DIRECTION_RIGHT;
        }
    },

    getWidthAuthority: function() {
        if (this.collapsed && this.collapsedHorizontal()) {
            return 1; 
        }

        return this.callParent();
    },

    getHeightAuthority: function() {
        if (this.collapsed && this.collapsedVertical()) {
            return 1; 
        }

        return this.callParent();
    },

    collapsedHorizontal: function () {
        var dir = this.getCollapsed();
        return dir === 'left' || dir === 'right';
    },

    collapsedVertical: function () {
        var dir = this.getCollapsed();
        return dir === 'top' || dir === 'bottom';
    },

    restoreDimension: function(){
        var dir = this.collapseDirection;
        
        
        return (dir === 'top' || dir === 'bottom') ? 'height' : 'width';    
    },

    
    getCollapsed: function() {
        var me = this;
        
        
        if (me.collapsed === true) {
            return me.collapseDirection;
        }
        return me.collapsed;
    },

    getState: function() {
        var me = this,
            state = me.callParent(),
            memento;

        state = me.addPropertyToState(state, 'collapsed');

        
        if (me.collapsed) {
            memento = me.collapseMemento;
            memento = memento && memento.data;

            if (me.collapsedVertical()) {
                if (state) {
                    delete state.height;
                }
                if (memento) {
                    state = me.addPropertyToState(state, 'height', memento.height);
                }
            } else {
                if (state) {
                    delete state.width;
                }
                if (memento) {
                    state = me.addPropertyToState(state, 'width', memento.width);
                }
            }
        }

        return state;
    },

    findReExpander: function (direction) {
        var me = this,
            c = Ext.Component,
            dockedItems = me.dockedItems.items,
            dockedItemCount = dockedItems.length,
            comp, i;
            
        
        if (me.collapseMode === 'mini') {
            return;
        }

        switch (direction) {
            case c.DIRECTION_TOP:
            case c.DIRECTION_BOTTOM:

                
                
                for (i = 0; i < dockedItemCount; i++) {
                    comp = dockedItems[i];
                    if (!comp.hidden) {
                        if (comp.isHeader && (!comp.dock || comp.dock === 'top' || comp.dock === 'bottom')) {
                            return comp;
                        }
                    }
                }
                break;

            case c.DIRECTION_LEFT:
            case c.DIRECTION_RIGHT:

                
                
                for (i = 0; i < dockedItemCount; i++) {
                    comp = dockedItems[i];
                    if (!comp.hidden) {
                        if (comp.isHeader && (comp.dock === 'left' || comp.dock === 'right')) {
                            return comp;
                        }
                    }
                }
                break;

            default:
                throw('Panel#findReExpander must be passed a valid collapseDirection');
        }
    },

    getReExpander: function (direction) {
        var me = this,
            collapseDir = direction || me.collapseDirection,
            reExpander = me.reExpander || me.findReExpander(collapseDir);

        me.expandDirection = me.getOppositeDirection(collapseDir);

        if (!reExpander) {
        
            me.reExpander = reExpander = me.createReExpander(collapseDir, {
                dock: collapseDir,
                cls: Ext.baseCSSPrefix + 'docked ' + me.baseCls + '-' + me.ui + '-collapsed',
                isCollapsedExpander: true
            });

            me.dockedItems.insert(0, reExpander);
        }
        return reExpander;
    },

    createReExpander: function(direction, defaults) {
        var me = this,
            isLeft = direction === 'left',
            isRight = direction === 'right',
            isVertical = isLeft || isRight,
            ownerCt = me.ownerCt,
            result = Ext.apply({
                hideMode: 'offsets',
                title: me.title || '&#160;',
                titleAlign: me.titleAlign,
                orientation: isVertical ? 'vertical' : 'horizontal',
                textCls: me.headerTextCls,
                icon: me.icon,
                iconCls: me.iconCls,
                glyph: me.glyph,
                baseCls: me.self.prototype.baseCls + '-header',
                ui: me.ui,
                frame: me.frame && me.frameHeader,
                ignoreParentFrame: me.frame || me.overlapHeader,
                ignoreBorderManagement: me.frame || me.ignoreHeaderBorderManagement,
                indicateDrag: me.draggable,
                collapseImmune: true,
                headerRole: me.headerRole,
                ownerCt: (ownerCt && me.collapseMode === 'placeholder') ? ownerCt : me,
                ownerLayout: me.componentLayout,
                margin: me.margin
            }, defaults);
            
        
        
        if (me.collapseMode === 'mini') {
            if (isVertical) {
                result.width = 1;
            } else {
                result.height = 1;
            }
        }

        
        
        
        if (!me.hideCollapseTool) {
            if (isLeft || (isRight && me.isPlaceHolderCollapse())) {
                
                
                result.titlePosition = 1;
            }
            result.tools = [{
                xtype: 'tool',
                type: 'expand-' + me.getOppositeDirection(direction),
                uiCls: ['top'],
                handler: me.toggleCollapse,
                scope: me
            }];
        }
        result = new Ext.panel.Header(result);
        result.addClsWithUI(me.getHeaderCollapsedClasses(result));
        return result;
    },

    
    
    getHeaderCollapsedClasses: function(header) {
        var me = this,
            collapsedCls = me.collapsedCls,
            collapsedClasses;

        collapsedClasses = [ collapsedCls, collapsedCls + '-' + header.getDockName()];
        if (me.border && (!me.frame || (me.frame && Ext.supports.CSS3BorderRadius))) {
            collapsedClasses.push(collapsedCls + '-border-' + header.getDockName());
        }
        return collapsedClasses;
    },

    
    beginCollapse: function() {
        var me = this,
            lastBox = me.lastBox,
            rendered = me.rendered,
            collapseMemento = me.collapseMemento || (me.collapseMemento = new Ext.util.Memento(me)),
            sizeModel = me.getSizeModel(),
            header = me.header,
            reExpander;

        
        
        
        
        
        
        
        
        
        
        
        collapseMemento.capture(['height', 'minHeight', 'width', 'minWidth']);
        if (lastBox) {
            collapseMemento.capture(me.restoreDimension(), lastBox, 'last.');
        }
        
        
        
        
        
        if (me.collapsedVertical()) {
            if (sizeModel.width.shrinkWrap) {
                me.width = rendered ? me.getWidth() : me.width || me.minWidth || 100;
            }
            delete me.height;
            me.minHeight = 0;
        } else if (me.collapsedHorizontal()) {
            if (sizeModel.height.shrinkWrap) {
                me.height = rendered ? me.getHeight() : me.height || me.minHeight || 100;
            }
            delete me.width;
            me.minWidth = 0;
        }

        if (me.ownerCt) {
            me.ownerCt.getLayout().beginCollapse(me);
        }

        
        
        if (!me.isPlaceHolderCollapse() && header !== false) {
            if (header === (reExpander = me.getReExpander())) {
                header.collapseImmune = true;
                header.getHierarchyState().collapseImmune = true;
                header.addClsWithUI(me.getHeaderCollapsedClasses(header));

                
                if (header.rendered) {
                    header.updateFrame();
                }
            } else if (reExpander.el) {
                
                reExpander.el.show();
                reExpander.hidden = false;
            }
        }
        if (me.resizer) {
            me.resizer.disable();
        }
    },

    beginExpand: function() {
        var me = this,
            lastBox = me.lastBox,
            collapseMemento = me.collapseMemento,
            restoreDimension = me.restoreDimension(),
            header = me.header,
            reExpander;

        if (collapseMemento) {
            collapseMemento.restore(['minHeight', 'minWidth', restoreDimension]);
            if (lastBox) {
                collapseMemento.restore(restoreDimension, true, lastBox, 'last.');
            }
        }

        if (me.ownerCt) {
            me.ownerCt.getLayout().beginExpand(me);
        }

        if (!me.isPlaceHolderCollapse() && header !== false) {
            
            if (header === (reExpander = me.getReExpander())) {
                delete header.collapseImmune;
                delete header.getHierarchyState().collapseImmune;
                header.removeClsWithUI(me.getHeaderCollapsedClasses(header));

                
                if (header.rendered) {
                    header.expanding = true;
                    header.updateFrame();
                    delete header.expanding;
                }
            } else {
                
                reExpander.hidden = true;
                reExpander.el.hide();
            }
        }
        if (me.resizer) {
            me.resizer.enable();
        }
    },

    
    collapse: function(direction, animate) {
        var me = this,
            collapseDir = direction || me.collapseDirection,
            ownerCt = me.ownerCt,
            layout = me.ownerLayout;

        if (me.isCollapsingOrExpanding) {
            return me;
        }

        if (arguments.length < 2) {
            animate = me.animCollapse;
        }

        if (me.collapsed || me.fireEvent('beforecollapse', me, direction, animate) === false) {
            return me;
        }
        
        if (layout && layout.onBeforeComponentCollapse) {
            if (layout.onBeforeComponentCollapse(me) === false) {
                return me;
            }
        }

        if (ownerCt && me.isPlaceHolderCollapse()) {
            return me.placeholderCollapse(direction, animate);
        }

        me.collapsed = collapseDir;
        me.beginCollapse();

        me.getHierarchyState().collapsed = true;
        me.fireHierarchyEvent('collapse');

        return me.doCollapseExpand(1, animate);
    },

    doCollapseExpand: function (flags, animate) {
        var me = this,
            originalAnimCollapse = me.animCollapse,
            ownerLayout = me.ownerLayout;

        
        
        me.animCollapse = animate;

        
        
        me.isCollapsingOrExpanding = flags;

        
        
        if (animate) {
            me.addCls(Ext.baseCSSPrefix + 'animating-size');
        }

        if (ownerLayout && !animate) {
            ownerLayout.onContentChange(me);
        } else {
            me.updateLayout({ isRoot: true });
        }

        
        me.animCollapse = originalAnimCollapse;

        return me;
    },

    
    afterCollapse: function(animated) {
        var me = this,
            ownerLayout = me.ownerLayout;

        me.isCollapsingOrExpanding = 0;
        me.updateCollapseTool();

        
        
        if (animated) {
            me.removeCls(Ext.baseCSSPrefix + 'animating-size');
        }

        if (ownerLayout && animated) {
            ownerLayout.onContentChange(me);
        }

        me.setHiddenDocked();
        me.fireEvent('collapse', me);
    },
    
    setHiddenDocked: function(){
        
        
        var me = this,
            toHide = me.hiddenOnCollapse,
            items = me.getDockedItems(),
            len = items.length,
            i = 0,
            item, reExpander;
            
        if (me.header !== false) {
            reExpander = me.getReExpander();
        }
            
        toHide.add(me.body);
        for (; i < len; i++) {
            item = items[i];
            if (item && item !== reExpander && item.el) {
                toHide.add(item.el);
            }
        }
        toHide.setStyle('visibility', 'hidden');
    },
    
    restoreHiddenDocked: function(){
        var toShow = this.hiddenOnCollapse;
        
        toShow.setStyle('visibility', '');
        toShow.clear();
    },

    getPlaceholder: function(direction) {
        var me = this,
            collapseDir = direction || me.collapseDirection,
            listeners = null,
            placeholder = me.placeholder,
            floatable = me.floatable,
            titleCollapse = me.titleCollapse;

        if (!placeholder) {
            if (floatable || (me.collapsible && titleCollapse)) {
                listeners = {
                    click: {
                        
                        fn: (!titleCollapse && floatable) ? me.floatCollapsedPanel : me.toggleCollapse,
                        element: 'el',
                        scope: me
                    }
                };
            }

            me.placeholder = placeholder = Ext.widget(me.createReExpander(collapseDir, {
                id: me.id + '-placeholder',
                listeners: listeners
            }));
        }

        
        if (!placeholder.placeholderFor) {
            
            if (!placeholder.isComponent) {
                me.placeholder = placeholder = me.lookupComponent(placeholder);
            }
            Ext.applyIf(placeholder, {
                margins: me.margins,
                placeholderFor: me
            });

            placeholder.addCls([Ext.baseCSSPrefix + 'region-collapsed-placeholder', Ext.baseCSSPrefix + 'region-collapsed-' + collapseDir + '-placeholder', me.collapsedCls]);
        }

        return placeholder;
    },

    placeholderCollapse: function(direction, animate) {
        var me = this,
            ownerCt = me.ownerCt,
            collapseDir = direction || me.collapseDirection,
            floatCls = Ext.baseCSSPrefix + 'border-region-slide-in',
            placeholder = me.getPlaceholder(collapseDir),
            slideInDirection;

        me.isCollapsingOrExpanding = 1;

        
        me.setHiddenState(true);
        me.collapsed = collapseDir;

        if (placeholder.rendered) {
            
            if (placeholder.el.dom.parentNode !== me.el.dom.parentNode) {
                me.el.dom.parentNode.insertBefore(placeholder.el.dom, me.el.dom);
            }

            placeholder.hidden = false;
            placeholder.el.show();
            ownerCt.updateLayout();
        } else {
            ownerCt.insert(ownerCt.items.indexOf(me), placeholder);
        }

        if (me.rendered) {
            
            me.el.setVisibilityMode(me.placeholderCollapseHideMode);
            if (animate) {
                me.el.addCls(floatCls);
                placeholder.el.hide();
                slideInDirection = me.convertCollapseDir(collapseDir);

                me.el.slideOut(slideInDirection, {
                    preserveScroll: true,
                    duration: Ext.Number.from(animate, Ext.fx.Anim.prototype.duration),
                    listeners: {
                        afteranimate: function() {
                            me.el.removeCls(floatCls);
                            
                            placeholder.el.show().setStyle('display', 'none').slideIn(slideInDirection, {
                                easing: 'linear',
                                duration: 100,
                                listeners: {
                                    afteranimate: function() {
                                        placeholder.focus();
                                        me.isCollapsingOrExpanding = 0;
                                        me.fireEvent('collapse', me);
                                    }
                                }
                            });
                        }
                    }
                });
            } else {
                me.el.hide();
                me.isCollapsingOrExpanding = 0;
                me.fireEvent('collapse', me);
            }
        } else {
            me.isCollapsingOrExpanding = 0;
            if (!me.preventCollapseFire) {
                me.fireEvent('collapse', me);
            }
        }

        return me;
    },

    floatCollapsedPanel: function() {
        var me = this,
            placeholder = me.placeholder,
            ps = placeholder.getSize(),
            myBox,
            floatCls = Ext.baseCSSPrefix + 'border-region-slide-in',
            collapsed = me.collapsed,
            layoutOwner = me.ownerCt || me,
            slideDirection;

        if (me.isSliding) {
            return;
        }

        
        if (me.el.hasCls(floatCls)) {
            me.slideOutFloatedPanel();
            return;
        }
        me.isSliding = true;

        
        placeholder.el.hide();
        placeholder.hidden = true;
        me.el.show();
        me.setHiddenState(false);
        me.collapsed = false;
        layoutOwner.updateLayout();
        myBox = me.getBox(false, true);

        
        placeholder.el.show();
        placeholder.hidden = false;
        me.el.hide();
        me.setHiddenState(true);
        me.collapsed = collapsed;
        layoutOwner.updateLayout();

        me.slideOutTask = me.slideOutTask || new Ext.util.DelayedTask(me.slideOutFloatedPanel, me);
        placeholder.el.on('mouseleave', me.onMouseLeaveFloated, me);
        me.el.on('mouseleave', me.onMouseLeaveFloated, me);
        placeholder.el.on('mouseenter', me.onMouseEnterFloated, me);
        me.el.on('mouseenter', me.onMouseEnterFloated, me);

        me.el.addCls(floatCls);
        me.floated = true;

        
        if (me.collapseTool) {
            me.collapseTool.el.hide();
        }

        switch (me.collapsed) {
            case 'top':
                me.setLocalXY(myBox.x, myBox.y + ps.height - 1);
                break;
            case 'right':
                me.setLocalXY(myBox.x - ps.width + 1, myBox.y);
                break;
            case 'bottom':
                me.setLocalXY(myBox.x, myBox.y - ps.height + 1);
                break;
            case 'left':
                me.setLocalXY(myBox.x + ps.width - 1, myBox.y);
                break;
        }
        slideDirection = me.convertCollapseDir(me.collapsed);

        
        
        me.floatedFromCollapse = me.collapsed;
        me.collapsed = false;
        me.setHiddenState(false);

        me.el.slideIn(slideDirection, {
            preserveScroll: true,
            duration: Ext.Number.from(me.animCollapse, Ext.fx.Anim.prototype.duration),
            listeners: {
                afteranimate: function() {
                    me.isSliding = false;
                    me.fireEvent('float', me);
                }
            }
        });
    },

    onMouseLeaveFloated: function(e) {
        this.slideOutTask.delay(500);
    },

    onMouseEnterFloated: function(e) {
        this.slideOutTask.cancel();
    },

    isLayoutRoot: function() {
        if (this.floatedFromCollapse) {
            return true;
        }
        return this.callParent();
    },

    slideOutFloatedPanel: function(preventAnimate) {
        var me = this,
            compEl = me.el,
            collapseDirection;

        if (me.isSliding || me.isDestroyed) {
            return;
        }

        me.isSliding = true;
        me.floated = false;

        me.slideOutFloatedPanelBegin();

        if (typeof me.collapsed == 'string') {
            collapseDirection = me.convertCollapseDir(me.collapsed);
        }

        compEl.slideOut(collapseDirection, {
            preserveScroll: true,
            duration: Ext.Number.from(me.animCollapse, Ext.fx.Anim.prototype.duration),
            autoEnd: preventAnimate === true,
            listeners: {
                afteranimate: function() {
                    me.slideOutFloatedPanelEnd();
                    
                    
                    me.el.removeCls(Ext.baseCSSPrefix + 'border-region-slide-in');
                }
            }
        });
    },

    
    slideOutFloatedPanelBegin: function() {
        var me = this,
            placeholderEl = me.placeholder.el,
            el = me.el;

        me.collapsed = me.floatedFromCollapse;
        me.setHiddenState(true);
        me.floatedFromCollapse = null;

        
        placeholderEl.un('mouseleave', me.onMouseLeaveFloated, me);
        el.un('mouseleave', me.onMouseLeaveFloated, me);
        placeholderEl.un('mouseenter', me.onMouseEnterFloated, me);
        el.un('mouseenter', me.onMouseEnterFloated, me);
    },

    
    slideOutFloatedPanelEnd: function(suppressEvents) {
        var me = this;

        if (me.collapseTool) {
            me.collapseTool.el.show();
        }
        me.slideOutTask.cancel();
        me.isSliding = false;
        if (!suppressEvents) {
            me.fireEvent('unfloat', me);
        }
    },

    
    expand: function(animate) {
        var me = this,
            layout = me.ownerLayout;

        if (me.isCollapsingOrExpanding) {
            return me;
        }

        if (!arguments.length) {
            animate = me.animCollapse;
        }

        if (!me.collapsed && !me.floatedFromCollapse) {
            return me;
        }

        
        if (me.fireEvent('beforeexpand', me, animate) === false) {
            return me;
        }
        
        if (layout && layout.onBeforeComponentExpand) {
            if (layout.onBeforeComponentExpand(me) === false) {
                return me;
            }
        }

        delete me.getHierarchyState().collapsed;

        if (me.isPlaceHolderCollapse()) {
            return me.placeholderExpand(animate);
        }

        me.restoreHiddenDocked();
        me.beginExpand();
        me.collapsed = false;

        return me.doCollapseExpand(2, animate);
    },

    placeholderExpand: function(animate) {
        var me = this,
            collapseDir = me.collapsed,
            floatCls = Ext.baseCSSPrefix + 'border-region-slide-in',
            finalPos,
            floatedPos,
            center = me.ownerLayout ? me.ownerLayout.centerRegion: null;

        
        if (Ext.AbstractComponent.layoutSuspendCount) {
            animate = false;
        }

        if (me.floatedFromCollapse) {
            floatedPos = me.getPosition(true);
            
            me.slideOutFloatedPanelBegin();
            me.slideOutFloatedPanelEnd();
            me.floated = false;
        }

        if (animate) {

            
            Ext.suspendLayouts();
            me.placeholder.hide();
            me.el.show();
            me.collapsed = false;
            me.setHiddenState(false);

            
            
            if (center && !floatedPos) {
                center.hidden = true;
            }

            Ext.resumeLayouts(true);
            center.hidden = false;
            me.el.addCls(floatCls);

            
            
            
            
            
            
            
            
            
            

            me.isCollapsingOrExpanding = 2;

            
            if (floatedPos) {
                finalPos = me.getXY();
                me.setLocalXY(floatedPos[0], floatedPos[1]);
                me.setXY([finalPos[0], finalPos[1]], {
                    duration: Ext.Number.from(animate, Ext.fx.Anim.prototype.duration),
                    listeners: {
                        afteranimate: function() {
                            me.el.removeCls(floatCls);
                            me.isCollapsingOrExpanding = 0;
                            me.fireEvent('expand', me);
                        }
                    }
                });
            }
            
            else {
                me.el.hide();
                me.placeholder.el.show();
                me.placeholder.hidden = false;

                
                me.setHiddenState(false);
                me.el.slideIn(me.convertCollapseDir(collapseDir), {
                    preserveScroll: true,
                    duration: Ext.Number.from(animate, Ext.fx.Anim.prototype.duration),
                    listeners: {
                        afteranimate: function() {
                            
                            
                            
                            
                            
                            me.el.removeCls(floatCls);
                            me.placeholder.hide();

                            
                            me.updateLayout();

                            me.isCollapsingOrExpanding = 0;
                            me.fireEvent('expand', me);
                        }
                    }
                });
            }

        } else {
            me.floated = me.collapsed = false;
            me.el.removeCls(floatCls);
            Ext.suspendLayouts();
            me.placeholder.hide();
            me.show();
            Ext.resumeLayouts(true);
            me.fireEvent('expand', me);
        }

        return me;
    },

    
    afterExpand: function(animated) {
        var me = this,
            ownerLayout = me.ownerLayout;

        me.isCollapsingOrExpanding = 0;
        me.updateCollapseTool();

        
        
        if (animated) {
            me.removeCls(Ext.baseCSSPrefix + 'animating-size');
        }

        if (ownerLayout && animated) {
            ownerLayout.onContentChange(me);
        }

        me.fireEvent('expand', me);
        me.fireHierarchyEvent('expand');
    },
    
    
    setBorder: function(border, targetEl) {
        if (targetEl) {
            
            return;
        }
        
        var me = this,
            header = me.header;
            
        if (!border) {
            border = 0;
        } else if (border === true) {
            border = '1px';
        } else {
            border = me.unitizeBox(border);
        }
        
        if (header) {
            if (header.isHeader) {
                header.setBorder(border);
            } else {
                header.border = border;
            }
        }
        
        if (me.rendered && me.bodyBorder !== false) {
            me.body.setStyle('border-width', border);
        }
        me.updateLayout();
        
        me.border = border;
    },

    
    toggleCollapse: function() {
        return (this.collapsed || this.floatedFromCollapse) ? this.expand() : this.collapse();
    },

    
    getKeyMap : function() {
        return this.keyMap || (this.keyMap = new Ext.util.KeyMap(Ext.apply({
            target: this.el
        }, this.keys)));
    },

    
    initDraggable : function() {
        var me = this;

        
        if (me.simpleDrag) {
            me.initSimpleDraggable();
        }
        
        else {
            
            me.dd = new Ext.panel.DD(me, Ext.isBoolean(me.draggable) ? null : me.draggable);
        }
    },

    
    initSimpleDraggable: function() {
        var me = this,
            ddConfig, dd;

        if (!me.header) {
            me.updateHeader(true);
        }

        
        if (me.header) {
            ddConfig = Ext.applyIf({
                el: me.el,
                delegate: '#' + Ext.escapeId(me.header.id)
            }, me.draggable);

            
            if (me.constrain || me.constrainHeader) {
                ddConfig.constrain = me.constrain;
                ddConfig.constrainDelegate = me.constrainHeader;
                ddConfig.constrainTo = me.constrainTo || me.container;
            }

            dd = me.dd = new Ext.util.ComponentDragger(me, ddConfig);
            me.relayEvents(dd, ['dragstart', 'drag', 'dragend']);
            if (me.maximized) {
                dd.disable();
            }
        }
    },
    
    
    
    ghostTools : function() {
        var tools = [],
            header = this.header,
            headerTools = header ? header.query('tool[hidden=false]') : [],
            t, tLen, tool;

        if (headerTools.length) {
            t = 0;
            tLen = headerTools.length;

            for (; t < tLen; t++) {
                tool = headerTools[t];

                
                
                
                
                tools.push({
                    type: tool.type
                });
            }
        } else {
            tools = [{
                type: 'placeholder'
            }];
        }
        return tools;
    },

    
    
    ghost: function(cls) {
        var me = this,
            ghostPanel = me.ghostPanel,
            box = me.getBox(),
            header = me.header,
            ghostHeader, tools, i;

        if (!ghostPanel) {
            me.ghostPanel = ghostPanel = Ext.widget(me.createGhost(cls));
        } else {
            ghostPanel.el.show();
        }
        ghostPanel.setHiddenState(false);
        ghostPanel.floatParent = me.floatParent;
        ghostPanel.toFront();
        if (header && !me.preventHeader) {
            ghostHeader = ghostPanel.header;
            
            ghostHeader.suspendLayouts();
            tools = ghostHeader.query('tool');
            for (i = tools.length; i--;) {
                ghostHeader.remove(tools[i]);
            }
            ghostPanel.addTool(me.ghostTools());
            ghostPanel.setTitle(me.title);
            ghostHeader.setTitlePosition(header.titlePosition);

            if (me.iconCls) {
                ghostPanel.setIconCls(me.iconCls);
            } else if (me.icon) {
                ghostPanel.setIcon(me.icon);
            } else if (me.glyph) {
                ghostPanel.setGlyph(me.glyph);
            }
            
            ghostHeader.addCls(Ext.baseCSSPrefix + 'header-ghost');
            ghostHeader.resumeLayouts();
        }

        ghostPanel.setPagePosition(box.x, box.y);
        ghostPanel.setSize(box.width, box.height);
        me.el.hide();
        return ghostPanel;
    },

    createGhost: function(cls) {
         var me = this,
             header = me.header,
             frame = me.frame && !me.alwaysFramed;

        return {
            xtype: 'panel',
            hidden: false,
            header: header ? {
                titleAlign: header.titleAlign
            } : null,
            ui: frame ? me.ui.replace(/-framed$/, '') : me.ui,
            id: me.id + '-ghost',
            renderTo: Ext.getBody(),
            
            
            resizable: false,
            floating: {
                shadow: false
            },
            frame: frame,
            alwaysFramed: me.alwaysFramed,
            overlapHeader: me.overlapHeader,
            headerPosition: me.headerPosition,
            baseCls: me.baseCls,
            cls: me.baseCls + '-ghost ' + (cls || '')
        };
    },

    
    unghost: function(show, matchPosition) {
        var me = this,
            ghostPanel = me.ghostPanel;
            
        if (!ghostPanel) {
            return;
        }
        if (show !== false) {
            
            
            me.el.show();
            if (matchPosition !== false) {
                me.setPagePosition(ghostPanel.getXY());
                if (me.hideMode == 'offsets') {
                    
                    delete me.el.hideModeStyles;
                }
            }
            Ext.defer(me.focus, 10, me);
        }
        ghostPanel.el.hide();
        ghostPanel.setHiddenState(true);
    },

    beginDrag: function() {
        if (this.floatingDescendants) {
            this.floatingDescendants.hide();
        }
    },

    endDrag: function() {
        if (this.floatingDescendants) {
            this.floatingDescendants.show();
        }
    },

    initResizable: function() {
        this.callParent(arguments);
        if (this.collapsed) {
            this.resizer.disable();
        }
    },

    
    convertCollapseDir: function(collapseDir) {
        return collapseDir.substr(0, 1);
    }
}, function() {
    this.prototype.animCollapse = Ext.enableFx;
});


Ext.define('Ext.tip.Tip', {
    extend:  Ext.panel.Panel ,

    alternateClassName: 'Ext.Tip',

    
    
    
    
    
    minWidth : 40,
    
    maxWidth : 500,
    
    shadow : "sides",

    
    defaultAlign : "tl-bl?",
    
    constrainPosition : true,

    
    autoRender: true,
    hidden: true,
    baseCls: Ext.baseCSSPrefix + 'tip',
    floating: {
        shadow: true,
        shim: true
    },
    focusOnToFront: false,

    
    closeAction: 'hide',

    
    alwaysFramed: true,

    frameHeader: false,

    initComponent: function() {
        var me = this;

        me.floating = Ext.apply( {}, {
            shadow: me.shadow,
            constrain: me.constrainPosition
        }, me.self.prototype.floating);
        me.callParent(arguments);

        
        me.constrain = me.constrain || me.constrainPosition;
    },

    
    showAt : function(xy){
        var me = this;
        this.callParent(arguments);
        
        if (me.isVisible()) {
            me.setPagePosition(xy[0], xy[1]);
            if (me.constrainPosition || me.constrain) {
                me.doConstrain();
            }
            me.toFront(true);
        }
    },

    
    initDraggable : function(){
        var me = this;
        me.draggable = {
            el: me.getDragEl(),
            delegate: me.header.el,
            constrain: me,
            constrainTo: me.el.dom.parentNode
        };
        
        Ext.Component.prototype.initDraggable.call(me);
    },

    
    ghost: undefined,
    unghost: undefined
});


Ext.define('Ext.tip.ToolTip', {
    extend:  Ext.tip.Tip ,
    alias: 'widget.tooltip',
    alternateClassName: 'Ext.ToolTip',
    
    
    
    autoHide: true,
    
    
    showDelay: 500,
    
    hideDelay: 200,
    
    dismissDelay: 5000,
    
    
    trackMouse: false,
    
    
    anchorToTarget: true,
    
    anchorOffset: 0,
    

    
    targetCounter: 0,
    quickShowInterval: 250,

    ariaRole: 'tooltip',

    
    initComponent: function() {
        var me = this;
        me.callParent(arguments);
        me.lastActive = new Date();
        me.setTarget(me.target);
        me.origAnchor = me.anchor;
    },

    
    onRender: function(ct, position) {
        var me = this;
        me.callParent(arguments);
        me.anchorCls = Ext.baseCSSPrefix + 'tip-anchor-' + me.getAnchorPosition();
        me.anchorEl = me.el.createChild({
            role: 'presentation',
            cls: Ext.baseCSSPrefix + 'tip-anchor ' + me.anchorCls
        });
    },

    
    setTarget: function(target) {
        var me = this,
            t = Ext.get(target),
            tg;

        if (me.target) {
            tg = Ext.get(me.target);
            me.mun(tg, 'mouseover', me.onTargetOver, me);
            me.mun(tg, 'mouseout', me.onTargetOut, me);
            me.mun(tg, 'mousemove', me.onMouseMove, me);
        }

        me.target = t;
        if (t) {

            me.mon(t, {
                
                
                freezeEvent: true,

                mouseover: me.onTargetOver,
                mouseout: me.onTargetOut,
                mousemove: me.onMouseMove,
                scope: me
            });
        }
        if (me.anchor) {
            me.anchorTarget = me.target;
        }
    },

    
    onMouseMove: function(e) {
        var me = this,
            t = me.delegate ? e.getTarget(me.delegate) : me.triggerElement = true,
            xy;
        if (t) {
            me.targetXY = e.getXY();
            if (t === me.triggerElement) {
                if (!me.hidden && me.trackMouse) {
                    xy = me.getTargetXY();
                    if (me.constrainPosition) {
                        xy = me.el.adjustForConstraints(xy, me.el.parent());
                    }
                    me.setPagePosition(xy);
                }
            } else {
                me.hide();
                me.lastActive = new Date(0);
                me.onTargetOver(e);
            }
        } else if ((!me.closable && me.isVisible()) && me.autoHide !== false) {
            me.hide();
        }
    },

    
    getTargetXY: function() {
        var me = this,
            mouseOffset,
            offsets, xy, dw, dh, de, bd, scrollX, scrollY, axy, sz, constrainPosition;
        if (me.delegate) {
            me.anchorTarget = me.triggerElement;
        }
        if (me.anchor) {
            me.targetCounter++;
            offsets = me.getOffsets();
            xy = (me.anchorToTarget && !me.trackMouse) ? me.getAlignToXY(me.anchorTarget, me.getAnchorAlign()) : me.targetXY;
            dw = Ext.Element.getViewWidth() - 5;
            dh = Ext.Element.getViewHeight() - 5;
            de = document.documentElement;
            bd = document.body;
            scrollX = (de.scrollLeft || bd.scrollLeft || 0) + 5;
            scrollY = (de.scrollTop || bd.scrollTop || 0) + 5;
            axy = [xy[0] + offsets[0], xy[1] + offsets[1]];
            sz = me.getSize();
            constrainPosition = me.constrainPosition;

            me.anchorEl.removeCls(me.anchorCls);

            if (me.targetCounter < 2 && constrainPosition) {
                if (axy[0] < scrollX) {
                    if (me.anchorToTarget) {
                        me.defaultAlign = 'l-r';
                        if (me.mouseOffset) {
                            me.mouseOffset[0] *= -1;
                        }
                    }
                    me.anchor = 'left';
                    return me.getTargetXY();
                }
                if (axy[0] + sz.width > dw) {
                    if (me.anchorToTarget) {
                        me.defaultAlign = 'r-l';
                        if (me.mouseOffset) {
                            me.mouseOffset[0] *= -1;
                        }
                    }
                    me.anchor = 'right';
                    return me.getTargetXY();
                }
                if (axy[1] < scrollY) {
                    if (me.anchorToTarget) {
                        me.defaultAlign = 't-b';
                        if (me.mouseOffset) {
                            me.mouseOffset[1] *= -1;
                        }
                    }
                    me.anchor = 'top';
                    return me.getTargetXY();
                }
                if (axy[1] + sz.height > dh) {
                    if (me.anchorToTarget) {
                        me.defaultAlign = 'b-t';
                        if (me.mouseOffset) {
                            me.mouseOffset[1] *= -1;
                        }
                    }
                    me.anchor = 'bottom';
                    return me.getTargetXY();
                }
            }

            me.anchorCls = Ext.baseCSSPrefix + 'tip-anchor-' + me.getAnchorPosition();
            me.anchorEl.addCls(me.anchorCls);
            me.targetCounter = 0;
            return axy;
        } else {
            mouseOffset = me.getMouseOffset();
            return (me.targetXY) ? [me.targetXY[0] + mouseOffset[0], me.targetXY[1] + mouseOffset[1]] : mouseOffset;
        }
    },

    getMouseOffset: function() {
        var me = this,
        offset = me.anchor ? [0, 0] : [15, 18];
        if (me.mouseOffset) {
            offset[0] += me.mouseOffset[0];
            offset[1] += me.mouseOffset[1];
        }
        return offset;
    },

    
    getAnchorPosition: function() {
        var me = this,
            m;
        if (me.anchor) {
            me.tipAnchor = me.anchor.charAt(0);
        } else {
            m = me.defaultAlign.match(/^([a-z]+)-([a-z]+)(\?)?$/);
            me.tipAnchor = m[1].charAt(0);
        }

        switch (me.tipAnchor) {
        case 't':
            return 'top';
        case 'b':
            return 'bottom';
        case 'r':
            return 'right';
        }
        return 'left';
    },

    
    getAnchorAlign: function() {
        switch (this.anchor) {
        case 'top':
            return 'tl-bl';
        case 'left':
            return 'tl-tr';
        case 'right':
            return 'tr-tl';
        default:
            return 'bl-tl';
        }
    },

    
    getOffsets: function() {
        var me = this,
            mouseOffset,
            offsets,
            ap = me.getAnchorPosition().charAt(0);
        if (me.anchorToTarget && !me.trackMouse) {
            switch (ap) {
            case 't':
                offsets = [0, 9];
                break;
            case 'b':
                offsets = [0, -13];
                break;
            case 'r':
                offsets = [ - 13, 0];
                break;
            default:
                offsets = [9, 0];
                break;
            }
        } else {
            switch (ap) {
            case 't':
                offsets = [ - 15 - me.anchorOffset, 30];
                break;
            case 'b':
                offsets = [ - 19 - me.anchorOffset, -13 - me.el.dom.offsetHeight];
                break;
            case 'r':
                offsets = [ - 15 - me.el.dom.offsetWidth, -13 - me.anchorOffset];
                break;
            default:
                offsets = [25, -13 - me.anchorOffset];
                break;
            }
        }
        mouseOffset = me.getMouseOffset();
        offsets[0] += mouseOffset[0];
        offsets[1] += mouseOffset[1];

        return offsets;
    },

    
    onTargetOver: function(e) {
        var me = this,
            delegate = me.delegate,
            t;

        if (me.disabled || e.within(me.target.dom, true)) {
            return;
        }
        t = delegate ? e.getTarget(delegate) : true;
        if (t) {
            me.triggerElement = t;
            me.triggerEvent = e;
            me.clearTimer('hide');
            me.targetXY = e.getXY();
            me.delayShow();
        }
    },

    
    delayShow: function() {
        var me = this;
        if (me.hidden && !me.showTimer) {
            if (Ext.Date.getElapsed(me.lastActive) < me.quickShowInterval) {
                me.show();
            } else {
                me.showTimer = Ext.defer(me.showFromDelay, me.showDelay, me);
            }
        }
        else if (!me.hidden && me.autoHide !== false) {
            me.show();
        }
    },
    
    showFromDelay: function(){
        this.fromDelayShow = true;
        this.show();
        delete this.fromDelayShow;
    },
    
    onShowVeto: function(){
        this.callParent();
        delete this.triggerElement;
        this.clearTimer('show');
    },

    
    onTargetOut: function(e) {
        var me = this,
            triggerEl = me.triggerElement,
            
            
            target = triggerEl === true ? me.target : triggerEl;

        
        
        if (me.disabled || !triggerEl || e.within(target, true)) {
            return;
        }
        if (me.showTimer) {
            me.clearTimer('show');
            me.triggerElement = null;
        }
        if (me.autoHide !== false) {
            me.delayHide();
        }
    },

    
    delayHide: function() {
        var me = this;
        if (!me.hidden && !me.hideTimer) {
            me.hideTimer = Ext.defer(me.hide, me.hideDelay, me);
        }
    },

    
    hide: function() {
        var me = this;
        me.clearTimer('dismiss');
        me.lastActive = new Date();
        if (me.anchorEl) {
            me.anchorEl.hide();
        }
        me.callParent(arguments);
        delete me.triggerElement;
    },

    
    show: function() {
        var me = this;

        
        
        this.callParent();
        if (this.hidden === false) {
            me.setPagePosition(-10000, -10000);

            if (me.anchor) {
                me.anchor = me.origAnchor;
            }
            
            if (!me.calledFromShowAt) {
                me.showAt(me.getTargetXY());
            }

            if (me.anchor) {
                me.syncAnchor();
                me.anchorEl.show();
            } else {
                me.anchorEl.hide();
            }
        }
    },

    
    showAt: function(xy) {
        var me = this;
        me.lastActive = new Date();
        me.clearTimers();
        me.calledFromShowAt = true;

        
        if (!me.isVisible()) {
            this.callParent(arguments);
        }

        
        if (me.isVisible()) {
            me.setPagePosition(xy[0], xy[1]);
            if (me.constrainPosition || me.constrain) {
                me.doConstrain();
            }
            me.toFront(true);
            me.el.sync(true);
            if (me.dismissDelay && me.autoHide !== false) {
                me.dismissTimer = Ext.defer(me.hide, me.dismissDelay, me);
            }
            if (me.anchor) {
                me.syncAnchor();
                if (!me.anchorEl.isVisible()) {
                    me.anchorEl.show();
                }
            } else {
                me.anchorEl.hide();
            }
        }
        delete me.calledFromShowAt;
    },

    
    syncAnchor: function() {
        var me = this,
            anchorPos,
            targetPos,
            offset;
        switch (me.tipAnchor.charAt(0)) {
        case 't':
            anchorPos = 'b';
            targetPos = 'tl';
            offset = [20 + me.anchorOffset, 1];
            break;
        case 'r':
            anchorPos = 'l';
            targetPos = 'tr';
            offset = [ - 1, 12 + me.anchorOffset];
            break;
        case 'b':
            anchorPos = 't';
            targetPos = 'bl';
            offset = [20 + me.anchorOffset, -1];
            break;
        default:
            anchorPos = 'r';
            targetPos = 'tl';
            offset = [1, 12 + me.anchorOffset];
            break;
        }
        me.anchorEl.alignTo(me.el, anchorPos + '-' + targetPos, offset);
        me.anchorEl.setStyle('z-index', parseInt(me.el.getZIndex(), 10) || 0 + 1).setVisibilityMode(Ext.Element.DISPLAY);
    },

    
    setPagePosition: function(x, y) {
        var me = this;
        me.callParent(arguments);
        if (me.anchor) {
            me.syncAnchor();
        }
    },

    _timerNames: {},
    
    clearTimer: function (name) {
        var me = this,
            names = me._timerNames,
            propName = names[name] || (names[name] = name + 'Timer'),
            timer = me[propName];

        if (timer) {
            clearTimeout(timer);
            me[propName] = null;
        }
    },

    
    clearTimers: function() {
        var me = this;
        me.clearTimer('show');
        me.clearTimer('dismiss');
        me.clearTimer('hide');
    },

    
    onShow: function() {
        var me = this;
        me.callParent();
        me.mon(Ext.getDoc(), 'mousedown', me.onDocMouseDown, me);
    },

    
    onHide: function() {
        var me = this;
        me.callParent();
        me.mun(Ext.getDoc(), 'mousedown', me.onDocMouseDown, me);
    },

    
    onDocMouseDown: function(e) {
        var me = this;
        if (!me.closable && !e.within(me.el.dom)) {
            me.disable();
            Ext.defer(me.doEnable, 100, me);
        }
    },

    
    doEnable: function() {
        if (!this.isDestroyed) {
            this.enable();
        }
    },

    
    onDisable: function() {
        this.callParent();
        this.clearTimers();
        this.hide();
    },

    beforeDestroy: function() {
        var me = this;
        me.clearTimers();
        Ext.destroy(me.anchorEl);
        delete me.anchorEl;
        delete me.target;
        delete me.anchorTarget;
        delete me.triggerElement;
        me.callParent();
    },

    
    onDestroy: function() {
        Ext.getDoc().un('mousedown', this.onDocMouseDown, this);
        this.callParent();
    }
});


Ext.define('Ext.tip.QuickTip', {
    extend:  Ext.tip.ToolTip ,
    alias: 'widget.quicktip',
    alternateClassName: 'Ext.QuickTip',

    

    
    interceptTitles : false,

    
    title: '&#160;',

    
    tagConfig : {
        namespace : 'data-',
        attribute : 'qtip',
        width : 'qwidth',
        target : 'target',
        title : 'qtitle',
        hide : 'hide',
        cls : 'qclass',
        align : 'qalign',
        anchor : 'anchor',
        showDelay: 'qshowDelay'
    },
    
    shrinkWrapDock: true,

    
    initComponent : function(){
        var me = this;

        me.target = me.target || Ext.getDoc();
        me.targets = me.targets || {};
        me.callParent();
    },

    
    register : function(config){
        var configs = Ext.isArray(config) ? config : arguments,
            i = 0,
            len = configs.length,
            target, j, targetLen;

        for (; i < len; i++) {
            config = configs[i];
            target = config.target;
            if (target) {
                if (Ext.isArray(target)) {
                    for (j = 0, targetLen = target.length; j < targetLen; j++) {
                        this.targets[Ext.id(target[j])] = config;
                    }
                } else{
                    this.targets[Ext.id(target)] = config;
                }
            }
        }
    },

    
    unregister : function(el){
        delete this.targets[Ext.id(el)];
    },

    
    cancelShow: function(el){
        var me = this,
            activeTarget = me.activeTarget;

        el = Ext.get(el).dom;
        if (me.isVisible()) {
            if (activeTarget && activeTarget.el == el) {
                me.hide();
            }
        } else if (activeTarget && activeTarget.el == el) {
            me.clearTimer('show');
        }
    },

    
    getTipCfg: function(e) {
        var t = e.getTarget(),
            titleText = t.title,
            cfg;

        if (this.interceptTitles && titleText && Ext.isString(titleText)) {
            t.qtip = titleText;
            t.removeAttribute("title");
            e.preventDefault();
            return {
                text: titleText
            };
        }
        else {
            cfg = this.tagConfig;
            t = e.getTarget('[' + cfg.namespace + cfg.attribute + ']');
            if (t) {
                return {
                    target: t,
                    text: t.getAttribute(cfg.namespace + cfg.attribute)
                };
            }
        }
    },

    
    onTargetOver : function(e){
        var me = this,
            target = e.getTarget(me.delegate),
            hasShowDelay,
            delay,
            elTarget,
            cfg,
            ns,
            tipConfig,
            autoHide,
            targets, targetEl, value, key;

        if (me.disabled) {
            return;
        }

        
        
        
        me.targetXY = e.getXY();

        
        if(!target || target.nodeType !== 1 || target == document.documentElement || target == document.body){
            return;
        }

        if (me.activeTarget && ((target == me.activeTarget.el) || Ext.fly(me.activeTarget.el).contains(target))) {
            
            
            
            
            if (me.targetTextEmpty()) {
                me.onShowVeto();
                delete me.activeTarget;
            } else {
                me.clearTimer('hide');
                me.show();
            }
            return;
        }

        if (target) {
            targets = me.targets;

            for (key in targets) {
                if (targets.hasOwnProperty(key)) {
                    value = targets[key];

                    targetEl = Ext.fly(value.target);
                    if (targetEl && (targetEl.dom === target || targetEl.contains(target))) {
                        elTarget = targetEl.dom;
                        break;
                    }
                }
            }

            if (elTarget) {
                me.activeTarget = me.targets[elTarget.id];
                me.activeTarget.el = target;
                me.anchor = me.activeTarget.anchor;
                if (me.anchor) {
                    me.anchorTarget = target;
                }
                hasShowDelay = parseInt(me.activeTarget.showDelay, 10);
                if (hasShowDelay) {
                    delay = me.showDelay;
                    me.showDelay = hasShowDelay;
                }
                me.delayShow();
                if (hasShowDelay) {
                    me.showDelay = delay;
                }
                return;
            }
        }

        
        elTarget = Ext.fly(target, '_quicktip-target');
        cfg = me.tagConfig;
        ns = cfg.namespace;
        tipConfig = me.getTipCfg(e);

        if (tipConfig) {

            
            
            if (tipConfig.target) {
                target = tipConfig.target;
                elTarget = Ext.fly(target, '_quicktip-target');
            }
            autoHide = elTarget.getAttribute(ns + cfg.hide);

            me.activeTarget = {
                el: target,
                text: tipConfig.text,
                width: +elTarget.getAttribute(ns + cfg.width) || null,
                autoHide: autoHide != "user" && autoHide !== 'false',
                title: elTarget.getAttribute(ns + cfg.title),
                cls: elTarget.getAttribute(ns + cfg.cls),
                align: elTarget.getAttribute(ns + cfg.align),
                showDelay: parseInt(elTarget.getAttribute(ns + cfg.showDelay), 10)
            };
            me.anchor = elTarget.getAttribute(ns + cfg.anchor);
            if (me.anchor) {
                me.anchorTarget = target;
            }
            hasShowDelay = parseInt(me.activeTarget.showDelay, 10);
            if (hasShowDelay) {
                delay = me.showDelay;
                me.showDelay = hasShowDelay;
            }
            me.delayShow();
            if (hasShowDelay) {
                me.showDelay = delay;
            }
        }
    },

    
    onTargetOut : function(e){
        var me = this,
            active = me.activeTarget,
            hasHideDelay,
            delay;

        
        
        if (active && e.within(me.activeTarget.el) && !me.getTipCfg(e)) {
            return;
        }

        me.clearTimer('show');
        delete me.activeTarget;
        if (me.autoHide !== false) {
            hasHideDelay = active && parseInt(active.hideDelay, 10);
            if (hasHideDelay) {
                delay = me.hideDelay;
                me.hideDelay = hasHideDelay;
            }
            me.delayHide();
            if (hasHideDelay) {
                me.hideDelay = delay;
            }
        }
    },
    
    targetTextEmpty: function(){
        var me = this,
            target = me.activeTarget,
            cfg = me.tagConfig,
            el, text;
            
         if (target) {
             el = target.el;
             if (el) {
                 text = el.getAttribute(cfg.namespace + cfg.attribute);
                 
                 
                 
                 if (!text && !me.targets[Ext.id(target.target)]) {
                     return true;
                 }
             }
         }
         return false;
    },
    
    show: function(){
        var me = this,
            fromDelay = me.fromDelayShow;
            
        
        
        if (fromDelay && me.targetTextEmpty()) {
            me.onShowVeto();
            delete me.activeTarget;
            return;
        }
        me.callParent(arguments);    
    },

    
    showAt : function(xy){
        var me = this,
            target = me.activeTarget,
            header = me.header,
            dismiss, cls;

        if (target) {
            if (!me.rendered) {
                me.render(Ext.getBody());
                me.activeTarget = target;
            }
            me.suspendLayouts();
            if (target.title) {
                me.setTitle(target.title);
                header.show();
            } else if (header) {
                header.hide();
            }
            me.update(target.text);
            me.autoHide = target.autoHide;
            dismiss = target.dismissDelay;
            
            me.dismissDelay = Ext.isNumber(dismiss) ? dismiss : me.dismissDelay;
            if (target.mouseOffset) {
                xy[0] += target.mouseOffset[0];
                xy[1] += target.mouseOffset[1];
            }

            cls = me.lastCls;
            if (cls) {
                me.removeCls(cls);
                delete me.lastCls;
            }

            cls = target.cls;
            if (cls) {
                me.addCls(cls);
                me.lastCls = cls;
            }

            me.setWidth(target.width);

            if (me.anchor) {
                me.constrainPosition = false;
            } else if (target.align) { 
                xy = me.getAlignToXY(target.el, target.align);
                me.constrainPosition = false;
            }else{
                me.constrainPosition = true;
            }
            me.resumeLayouts(true);
        }
        me.callParent([xy]);
    },

    
    hide: function(){
        delete this.activeTarget;
        this.callParent();
    }
});


Ext.define('Ext.tip.QuickTipManager', {
                                   
    singleton: true,
    alternateClassName: 'Ext.QuickTips',
    disabled: false,

    
    init : function (autoRender, config) {
        var me = this;

        if (!me.tip) {
            if (!Ext.isReady) {
                Ext.onReady(function(){
                    Ext.tip.QuickTipManager.init(autoRender, config);
                });
                return false;
            }

            var tipConfig = Ext.apply({
                disabled: me.disabled,
                id: 'ext-quicktips-tip'
            }, config),
                className = tipConfig.className,
                xtype = tipConfig.xtype;

            if (className) {
                delete tipConfig.className;
            } else if (xtype) {
                className = 'widget.' + xtype;
                delete tipConfig.xtype;
            }

            if (autoRender !== false) {
                tipConfig.renderTo = document.body;

            }

            me.tip = Ext.create(className || 'Ext.tip.QuickTip', tipConfig);

            
            
            Ext.quickTipsActive = true;
        }
    },

    
    destroy: function() {
        Ext.destroy(this.tip);
        this.tip = undefined;
    },

    
    ddDisable : function() {
        var me = this,
            tip = me.tip;

        
        if (tip && !me.disabled) {
            tip.disable();
        }
    },

    
    ddEnable : function() {
        var me = this,
            tip = me.tip;

        
        if (tip && !me.disabled) {
            tip.enable();
        }
    },

    
    enable : function(){
        var me = this,
            tip = me.tip;

        if (tip) {
            tip.enable();
        }
        me.disabled = false;
    },

    
    disable : function(){
        var me = this,
            tip = me.tip;

        if(tip){
            tip.disable();
        }
        me.disabled = true;
    },

    
    isEnabled : function(){
        var tip = this.tip;

        return tip !== undefined && !tip.disabled;
    },

    
    getQuickTip : function(){
        return this.tip;
    },

    
    register : function(){
        var tip = this.tip;

        tip.register.apply(tip, arguments);
    },

    
    unregister : function(){
        var tip = this.tip;

        tip.unregister.apply(tip, arguments);
    },

    
    tips : function(){
        var tip = this.tip;

        tip.register.apply(tip, arguments);
    }
});


Ext.define('Ext.app.Application', {
    extend:  Ext.app.Controller ,

               
                                 
      

    

    

    
    scope: undefined,

    
    enableQuickTips: true,

    
    appFolder: 'app',
    

    
    appProperty: 'app',

    
    namespaces: [],

    
    autoCreateViewport: false,

    
    paths: null,
    
    onClassExtended: function(cls, data, hooks) {
        var Controller = Ext.app.Controller,
            proto = cls.prototype,
            requires = [],
            onBeforeClassCreated, paths, namespace, ns, appFolder;
        
        
        
        namespace = data.name      || cls.superclass.name;
        appFolder = data.appFolder || cls.superclass.appFolder;
        
        if (namespace) {
            data.$namespace = namespace;
            Ext.app.addNamespaces(namespace);
        }

        if (data.namespaces) {
            Ext.app.addNamespaces(data.namespaces);
        }

        if (!data['paths processed']) {
            if (namespace && appFolder) {
                Ext.Loader.setPath(namespace, appFolder);
            }
            
            paths = data.paths;

            if (paths) {
                for (ns in paths) {
                    if (paths.hasOwnProperty(ns)) {
                        Ext.Loader.setPath(ns, paths[ns]);
                    }
                }
            }
        }
        else {
            delete data['paths processed'];
        }

        if (data.autoCreateViewport) {
            
            Controller.processDependencies(proto, requires, namespace, 'view', ['Viewport']);
        }

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

    
    constructor: function(config) {
        var me = this;


        me.callParent(arguments);

        me.doInit(me);

        me.initNamespace();
        me.initControllers();
        me.onBeforeLaunch();
        
        me.finishInitControllers();
    },

    initNamespace: function() {
        var me = this,
            appProperty = me.appProperty,
            ns;

        ns = Ext.namespace(me.name);

        if (ns) {
            ns.getApplication = function() {
                return me;
            };

            if (appProperty) {
                if (!ns[appProperty]) {
                    ns[appProperty] = me;
                }
            }
        }
    },

    initControllers: function() {
        var me = this,
            controllers = Ext.Array.from(me.controllers);

        me.controllers = new Ext.util.MixedCollection();

        for (var i = 0, ln = controllers.length; i < ln; i++) {
            me.getController(controllers[i]);
        }
    },
    
    finishInitControllers: function() {
        var me = this,
            controllers, i, l;
        
        controllers = me.controllers.getRange();
        
        for (i = 0, l = controllers.length; i < l; i++) {
            controllers[i].finishInit(me);
        }
    },

    
    launch: Ext.emptyFn,

    
    onBeforeLaunch: function() {
        var me = this,
            controllers, c, cLen, controller;

        if (me.enableQuickTips) {
            me.initQuickTips();
        }

        if (me.autoCreateViewport) {
            me.initViewport();
        }

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

    getModuleClassName: function(name, kind) {
        return Ext.app.Controller.getFullName(name, kind, this.name).absoluteName;
    },

    initQuickTips: function() {
        Ext.tip.QuickTipManager.init();
    },

    initViewport: function() {
        var viewport = this.getView('Viewport');

        if (viewport) {
            viewport.create();
        }
    },

    getController: function(name) {
        var me          = this,
            controllers = me.controllers,
            className, controller;

        controller = controllers.get(name);

        if (!controller) {
            className  = me.getModuleClassName(name, 'controller');

            controller = Ext.create(className, {
                application: me,
                id:          name
            });

            controllers.add(controller);

            if (me._initialized) {
                controller.doInit(me);
            }
        }

        return controller;
    },

    getApplication: function() {
        return this;
    }
});


Ext.define('Ext.app.domain.Controller', {
    extend:  Ext.app.EventDomain ,
    singleton: true,

               
                            
      

    type: 'controller',
    idProperty: 'id',

    constructor: function() {
        var me = this;
        
        me.callParent();
        me.monitor(Ext.app.Controller);
    }
});


Ext.define('Ext.direct.Provider', {
   alias: 'direct.provider',

    mixins: {
        observable:  Ext.util.Observable 
    },
    
    isProvider: true,

   
    
    
    
    constructor: function(config) {
        var me = this;
        
        Ext.apply(me, config);
        
        Ext.applyIf(me, {
            id: Ext.id(null, 'provider-')
        });

        me.addEvents(
            
            'connect',
            
            
            'disconnect',
            
            
            'data',
            
            
            'exception'
        );

        me.mixins.observable.constructor.call(me, config);
    },

    
    isConnected: function() {
        return false;
    },

    
    connect: Ext.emptyFn,

    
    disconnect: Ext.emptyFn
});



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.button.Split', {

    
    alias: 'widget.splitbutton',

    extend:  Ext.button.Button ,
    alternateClassName: 'Ext.SplitButton',
    
    
    
    

    
    arrowCls      : 'split',
    split         : true,

    
    initComponent : function(){
        this.callParent();
        
        this.addEvents("arrowclick");
    },

    
    setArrowHandler : function(handler, scope){
        this.arrowHandler = handler;
        this.scope = scope;
    },

    
    onClick : function(e) {
        var me = this;
        
        me.doPreventDefault(e);
        if (!me.disabled) {
            if (me.overMenuTrigger) {
                
                
                e.preventDefault();
                me.maybeShowMenu();
                me.fireEvent("arrowclick", me, e);
                if (me.arrowHandler) {
                    me.arrowHandler.call(me.scope || me, me, e);
                }
            } else {
                me.doToggle();
                me.fireHandler(e);
            }
        }
    }
});


Ext.define('Ext.button.Cycle', {

    

    alias: 'widget.cycle',

    extend:  Ext.button.Split ,
    alternateClassName: 'Ext.CycleButton',

    

    
    
    
    
    

    
    

    
    getButtonText: function(item) {
        var me = this,
            text = '';

        if (item && me.showText === true) {
            if (me.prependText) {
                text += me.prependText;
            }
            text += item.text;
            return text;
        }
        return me.text;
    },

    
    setActiveItem: function(item, suppressEvent) {
        var me = this;

        if (!Ext.isObject(item)) {
            item = me.menu.getComponent(item);
        }
        if (item) {
            if (!me.rendered) {
                me.text = me.getButtonText(item);
                me.iconCls = item.iconCls;
                me.glyph = item.glyph;
            } else {
                me.setText(me.getButtonText(item));
                me.setIconCls(item.iconCls);
                me.setGlyph(item.glyph);
            }
            me.activeItem = item;
            if (!item.checked) {
                item.setChecked(true, false);
            }
            if (me.forceIcon) {
                me.setIconCls(me.forceIcon);
            }
            if (me.forceGlyph) {
                me.setGlyph(me.forceGlyph);
            }
            if (!suppressEvent) {
                me.fireEvent('change', me, item);
            }
        }
    },

    
    getActiveItem: function() {
        return this.activeItem;
    },

    
    initComponent: function() {
        var me      = this,
            checked = 0,
            items,
            i, iLen, item;

        me.addEvents(
            
            "change"
        );

        if (me.changeHandler) {
            me.on('change', me.changeHandler, me.scope || me);
            delete me.changeHandler;
        }

        
        
        items = (me.menu.items || []).concat(me.items || []);
        me.menu = Ext.applyIf({
            cls: Ext.baseCSSPrefix + 'cycle-menu',
            items: []
        }, me.menu);

        iLen = items.length;

        
        for (i = 0; i < iLen; i++) {
            item = items[i];

            item = Ext.applyIf({
                group        : me.id,
                itemIndex    : i,
                checkHandler : me.checkHandler,
                scope        : me,
                checked      : item.checked || false
            }, item);

            me.menu.items.push(item);

            if (item.checked) {
                checked = i;
            }
        }

        me.itemCount = me.menu.items.length;
        me.callParent(arguments);
        me.on('click', me.toggleSelected, me);
        me.setActiveItem(checked, me);

        
        if (me.width && me.showText) {
            me.addCls(Ext.baseCSSPrefix + 'cycle-fixed-width');
        }
    },

    
    checkHandler: function(item, pressed) {
        if (pressed) {
            this.setActiveItem(item);
        }
    },

    
    toggleSelected: function() {
        var me = this,
            m = me.menu,
            checkItem;

        checkItem = me.activeItem.next(':not([disabled])') || m.items.getAt(0);
        checkItem.setChecked(true);
    }
});


Ext.define('Ext.chart.Callout', {

    

    

    constructor: function(config) {
        if (config.callouts) {
            config.callouts.styles = Ext.applyIf(config.callouts.styles || {}, {
                color: "#000",
                font: "11px Helvetica, sans-serif"
            });
            this.callouts = Ext.apply(this.callouts || {}, config.callouts);
            this.calloutsArray = [];
        }
    },

    renderCallouts: function() {
        if (!this.callouts) {
            return;
        }

        var me = this,
            items = me.items,
            animate = me.chart.animate,
            config = me.callouts,
            styles = config.styles,
            group = me.calloutsArray,
            store = me.chart.getChartStore(),
            len = store.getCount(),
            ratio = items.length / len,
            previouslyPlacedCallouts = [],
            i,
            count,
            j,
            p,
            item,
            label,
            storeItem,
            display;
            
        for (i = 0, count = 0; i < len; i++) {
            for (j = 0; j < ratio; j++) {
                item = items[count];
                label = group[count];
                storeItem = store.getAt(i);
                
                display = (!config.filter || config.filter(storeItem));
                
                if (!display && !label) {
                    count++;
                    continue;               
                }
                
                if (!label) {
                    group[count] = label = me.onCreateCallout(storeItem, item, i, display, j, count);
                }
                for (p in label) {
                    if (label[p] && label[p].setAttributes) {
                        label[p].setAttributes(styles, true);
                    }
                }
                if (!display) {
                    for (p in label) {
                        if (label[p]) {
                            if (label[p].setAttributes) {
                                label[p].setAttributes({
                                    hidden: true
                                }, true);
                            } else if(label[p].setVisible) {
                                label[p].setVisible(false);
                            }
                        }
                    }
                }
                if (config && config.renderer) {
                    config.renderer(label, storeItem);
                }
                me.onPlaceCallout(label, storeItem, item, i, display, animate,
                                  j, count, previouslyPlacedCallouts);
                previouslyPlacedCallouts.push(label);
                count++;
            }
        }
        this.hideCallouts(count);
    },

    onCreateCallout: function(storeItem, item, i, display) {
        var me = this,
            group = me.calloutsGroup,
            config = me.callouts,
            styles = (config ? config.styles : undefined),
            width = (styles ? styles.width : 0),
            height = (styles ? styles.height : 0),
            chart = me.chart,
            surface = chart.surface,
            calloutObj = {
                
                
                lines: false
            };

        calloutObj.lines = surface.add(Ext.apply({},
        {
            type: 'path',
            path: 'M0,0',
            stroke: me.getLegendColor() || '#555'
        },
        styles));

        if (config.items) {
            calloutObj.panel = new Ext.Panel({
                style: "position: absolute;",    
                width: width,
                height: height,
                items: config.items,
                renderTo: chart.el
            });
        }

        return calloutObj;
    },

    hideCallouts: function(index) {
        var calloutsArray = this.calloutsArray,
            len = calloutsArray.length,
            co,
            p;
        while (len-->index) {
            co = calloutsArray[len];
            for (p in co) {
                if (co[p]) {
                    co[p].hide(true);
                }
            }
        }
    }
});


Ext.define('Ext.draw.CompositeSprite', {

    

    extend:  Ext.util.MixedCollection ,
    mixins: {
        animate:  Ext.util.Animate 
    },
    autoDestroy: false,
    
    
    isCompositeSprite: true,
    constructor: function(config) {
        var me = this;
        
        config = config || {};
        Ext.apply(me, config);

        me.addEvents(
            
            'mousedown',
            
            'mouseup',
            
            'mouseover',
            
            'mouseout',
            
            'click'
        );
        me.id = Ext.id(null, 'ext-sprite-group-');
        me.callParent();
    },

    
    onClick: function(e) {
        this.fireEvent('click', e);
    },

    
    onMouseUp: function(e) {
        this.fireEvent('mouseup', e);
    },

    
    onMouseDown: function(e) {
        this.fireEvent('mousedown', e);
    },

    
    onMouseOver: function(e) {
        this.fireEvent('mouseover', e);
    },

    
    onMouseOut: function(e) {
        this.fireEvent('mouseout', e);
    },

    attachEvents: function(o) {
        var me = this;
        
        o.on({
            scope: me,
            mousedown: me.onMouseDown,
            mouseup: me.onMouseUp,
            mouseover: me.onMouseOver,
            mouseout: me.onMouseOut,
            click: me.onClick
        });
    },

    
    add: function(key, o) {
        var result = this.callParent(arguments);
        this.attachEvents(result);
        return result;
    },

    insert: function(index, key, o) {
        return this.callParent(arguments);
    },

    
    remove: function(o) {
        var me = this;
        
        o.un({
            scope: me,
            mousedown: me.onMouseDown,
            mouseup: me.onMouseUp,
            mouseover: me.onMouseOver,
            mouseout: me.onMouseOut,
            click: me.onClick
        });
        return me.callParent(arguments);
    },
    
    
    getBBox: function() {
        var i = 0,
            sprite,
            bb,
            items = this.items,
            len = this.length,
            infinity = Infinity,
            minX = infinity,
            maxHeight = -infinity,
            minY = infinity,
            maxWidth = -infinity,
            maxWidthBBox, maxHeightBBox;
        
        for (; i < len; i++) {
            sprite = items[i];
            if (sprite.el && ! sprite.bboxExcluded) {
                bb = sprite.getBBox();
                minX = Math.min(minX, bb.x);
                minY = Math.min(minY, bb.y);
                maxHeight = Math.max(maxHeight, bb.height + bb.y);
                maxWidth = Math.max(maxWidth, bb.width + bb.x);
            }
        }
        
        return {
            x: minX,
            y: minY,
            height: maxHeight - minY,
            width: maxWidth - minX
        };
    },

    
    setAttributes: function(attrs, redraw) {
        var i = 0,
            items = this.items,
            len = this.length;
            
        for (; i < len; i++) {
            items[i].setAttributes(attrs, redraw);
        }
        return this;
    },

    
    hide: function(redraw) {
        var i = 0,
            items = this.items,
            len = this.length;
            
        for (; i < len; i++) {
            items[i].hide(redraw);
        }
        return this;
    },

    
    show: function(redraw) {
        var i = 0,
            items = this.items,
            len = this.length;
            
        for (; i < len; i++) {
            items[i].show(redraw);
        }
        return this;
    },

    
    redraw: function() {
        var me = this,
            i = 0,
            items = me.items,
            surface = me.getSurface(),
            len = me.length;
        
        if (surface) {
            for (; i < len; i++) {
                surface.renderItem(items[i]);
            }
        }
        return me;
    },

    
    setStyle: function(obj) {
        var i = 0,
            items = this.items,
            len = this.length,
            item, el;
            
        for (; i < len; i++) {
            item = items[i];
            el = item.el;
            if (el) {
                el.setStyle(obj);
            }
        }
    },

    
    addCls: function(obj) {
        var i = 0,
            items = this.items,
            surface = this.getSurface(),
            len = this.length;
        
        if (surface) {
            for (; i < len; i++) {
                surface.addCls(items[i], obj);
            }
        }
    },

    
    removeCls: function(obj) {
        var i = 0,
            items = this.items,
            surface = this.getSurface(),
            len = this.length;
        
        if (surface) {
            for (; i < len; i++) {
                surface.removeCls(items[i], obj);
            }
        }
    },
    
    
    getSurface: function(){
        var first = this.first();
        if (first) {
            return first.surface;
        }
        return null;
    },
    
    
    destroy: function(){
        var me = this,
            surface = me.getSurface(),
            destroySprites = me.autoDestroy,
            item;
            
        if (surface) {
            while (me.getCount() > 0) {
                item = me.first();
                me.remove(item);
                surface.remove(item, destroySprites);
            }
        }
        me.clearListeners();
    }
});


Ext.define('Ext.draw.Surface', {

    

    mixins: {
        observable:  Ext.util.Observable 
    },

                                           
                                                                                                                         

    separatorRe: /[, ]+/,
    
    enginePriority: ['Svg', 'Vml'],

    statics: {
        
        create: function(config, enginePriority) {
            enginePriority = enginePriority || this.prototype.enginePriority;

            var i = 0,
                len = enginePriority.length;

            for (; i < len; i++) {
                if (Ext.supports[enginePriority[i]]) {
                    return Ext.create('Ext.draw.engine.' + enginePriority[i], config);
                }
            }
            return false;
        },
        
        
        save: function(surface, config) {
            config = config || {};
            var exportTypes = {
                    'image/png': 'Image',
                    'image/jpeg': 'Image',
                    'image/svg+xml': 'Svg'
                },
                prefix = exportTypes[config.type] || 'Svg',
                exporter = Ext.draw.engine[prefix + 'Exporter'];           

            return exporter.generate(surface, config);
            
        }
    },

    

    
    availableAttrs: {
        blur: 0,
        "clip-rect": "0 0 1e9 1e9",
        cursor: "default",
        cx: 0,
        cy: 0,
        'dominant-baseline': 'auto',
        fill: "none",
        "fill-opacity": 1,
        font: '10px "Arial"',
        "font-family": '"Arial"',
        "font-size": "10",
        "font-style": "normal",
        "font-weight": 400,
        gradient: "",
        height: 0,
        hidden: false,
        href: "http://sencha.com/",
        opacity: 1,
        path: "M0,0",
        radius: 0,
        rx: 0,
        ry: 0,
        scale: "1 1",
        src: "",
        stroke: "none",
        "stroke-dasharray": "",
        "stroke-linecap": "butt",
        "stroke-linejoin": "butt",
        "stroke-miterlimit": 0,
        "stroke-opacity": 1,
        "stroke-width": 1,
        target: "_blank",
        text: "",
        "text-anchor": "middle",
        title: "Ext Draw",
        width: 0,
        x: 0,
        y: 0,
        zIndex: 0
    },

    
    

    container: undefined,
    height: 352,
    width: 512,
    x: 0,
    y: 0,

    

    
    orderSpritesByZIndex: true,


    
    constructor: function(config) {
        var me = this;
        config = config || {};
        Ext.apply(me, config);

        me.domRef = Ext.getDoc().dom;

        me.customAttributes = {};

        me.addEvents(
            
            'mousedown',
            
            'mouseup',
            
            'mouseover',
            
            'mouseout',
            
            'mousemove',
            
            'mouseenter',
            
            'mouseleave',
            
            'click',
            
            'dblclick'
        );

        me.mixins.observable.constructor.call(me);

        me.getId();
        me.initGradients();
        me.initItems();
        if (me.renderTo) {
            me.render(me.renderTo);
            delete me.renderTo;
        }
        me.initBackground(config.background);
    },

    
    
    initSurface: Ext.emptyFn,

    
    
    renderItem: Ext.emptyFn,

    
    renderItems: Ext.emptyFn,

    
    setViewBox: function (x, y, width, height) {
        if (isFinite(x) && isFinite(y) && isFinite(width) && isFinite(height)) {
            this.viewBox = {x: x, y: y, width: width, height: height};
            this.applyViewBox();
        }
    },

    
    addCls: Ext.emptyFn,

    
    removeCls: Ext.emptyFn,

    
    setStyle: Ext.emptyFn,

    
    initGradients: function() {
        if (this.hasOwnProperty('gradients')) {
            var gradients = this.gradients,
                fn = this.addGradient,
                g, gLen;

            if (gradients) {
                for (g = 0, gLen = gradients.length; g < gLen; g++) {
                    if (fn.call(this, gradients[g], g, gLen) === false) {
                        break;
                    }
                }
            }
        }
    },

    
    initItems: function() {
        var items = this.items;
        this.items = new Ext.draw.CompositeSprite();
        this.items.autoDestroy = true;
        this.groups = new Ext.draw.CompositeSprite();
        if (items) {
            this.add(items);
        }
    },

    
    initBackground: function(config) {
        var me = this,
            width = me.width,
            height = me.height,
            gradientId, gradient;
        if (Ext.isString(config)) {
            config = {
                fill : config
            };
        }
        if (config) {
            if (config.gradient) {
                gradient = config.gradient;
                gradientId = gradient.id;
                me.addGradient(gradient);
                me.background = me.add({
                    type: 'rect',
                    isBackground: true,
                    x: 0,
                    y: 0,
                    width: width,
                    height: height,
                    fill: 'url(#' + gradientId + ')',
                    zIndex: -1
                });
            } else if (config.fill) {
                me.background = me.add({
                    type: 'rect',
                    isBackground: true,
                    x: 0,
                    y: 0,
                    width: width,
                    height: height,
                    fill: config.fill,
                    zIndex: -1
                });
            } else if (config.image) {
                me.background = me.add({
                    type: 'image',
                    isBackground: true,
                    x: 0,
                    y: 0,
                    width: width,
                    height: height,
                    src: config.image,
                    zIndex: -1
                });
            }
            
            me.background.bboxExcluded = true;
        }
    },

    
    setSize: function(w, h) {
        this.applyViewBox();
    },

    
    scrubAttrs: function(sprite) {
        var i,
            attrs = {},
            exclude = {},
            sattr = sprite.attr;
        for (i in sattr) {
            
            if (this.translateAttrs.hasOwnProperty(i)) {
                
                attrs[this.translateAttrs[i]] = sattr[i];
                exclude[this.translateAttrs[i]] = true;
            }
            else if (this.availableAttrs.hasOwnProperty(i) && !exclude[i]) {
                
                attrs[i] = sattr[i];
            }
        }
        return attrs;
    },

    
    onClick: function(e) {
        this.processEvent('click', e);
    },
    
    
    onDblClick: function(e) {
        this.processEvent('dblclick', e);
    },

    
    onMouseUp: function(e) {
        this.processEvent('mouseup', e);
    },

    
    onMouseDown: function(e) {
        this.processEvent('mousedown', e);
    },

    
    onMouseOver: function(e) {
        this.processEvent('mouseover', e);
    },

    
    onMouseOut: function(e) {
        this.processEvent('mouseout', e);
    },

    
    onMouseMove: function(e) {
        this.fireEvent('mousemove', e);
    },

    
    onMouseEnter: Ext.emptyFn,

    
    onMouseLeave: Ext.emptyFn,

    
    addGradient: Ext.emptyFn,

    
    add: function() {
        var args = Array.prototype.slice.call(arguments),
            sprite,
            hasMultipleArgs = args.length > 1,
            items,
            results,
            i,
            ln,
            item;
            
        if (hasMultipleArgs || Ext.isArray(args[0])) {
            items = hasMultipleArgs ? args : args[0];
            results = [];

            for (i = 0, ln = items.length; i < ln; i++) {
                item = items[i];
                item = this.add(item);
                results.push(item);
            }

            return results;
        }
        sprite = this.prepareItems(args[0], true)[0];
        this.insertByZIndex(sprite);
        this.onAdd(sprite);
        return sprite;
    },

    
    insertByZIndex: function(sprite) {
        var me = this,
            sprites = me.items.items,
            len = sprites.length,
            ceil = Math.ceil,
            zIndex = sprite.attr.zIndex,
            idx = len,
            high = idx - 1,
            low = 0,
            otherZIndex;

        if (me.orderSpritesByZIndex && len && zIndex < sprites[high].attr.zIndex) {
            
            while (low <= high) {
                idx = ceil((low + high) / 2);
                otherZIndex = sprites[idx].attr.zIndex;
                if (otherZIndex > zIndex) {
                    high = idx - 1;
                }
                else if (otherZIndex < zIndex) {
                    low = idx + 1;
                }
                else {
                    break;
                }
            }
            
            while (idx < len && sprites[idx].attr.zIndex <= zIndex) {
                idx++;
            }
        }

        me.items.insert(idx, sprite);
        return idx;
    },

    onAdd: function(sprite) {
        var group = sprite.group,
            draggable = sprite.draggable,
            groups, ln, i;
        if (group) {
            groups = [].concat(group);
            ln = groups.length;
            for (i = 0; i < ln; i++) {
                group = groups[i];
                this.getGroup(group).add(sprite);
            }
            delete sprite.group;
        }
        if (draggable) {
            sprite.initDraggable();
        }
    },

    
    remove: function(sprite, destroySprite) {
        if (sprite) {
            this.items.remove(sprite);

            var groups = [].concat(this.groups.items),
                gLen   = groups.length,
                g;

            for (g = 0; g < gLen; g++) {
                groups[g].remove(sprite);
            }

            sprite.onRemove();
            if (destroySprite === true) {
                sprite.destroy();
            }
        }
    },

    
    removeAll: function(destroySprites) {
        var items = this.items.items,
            ln = items.length,
            i;
        for (i = ln - 1; i > -1; i--) {
            this.remove(items[i], destroySprites);
        }
    },

    onRemove: Ext.emptyFn,

    onDestroy: Ext.emptyFn,

    
    applyViewBox: function() {
        var me = this,
            viewBox = me.viewBox,
            width = me.width || 1, 
            height = me.height || 1,
            viewBoxX, viewBoxY, viewBoxWidth, viewBoxHeight,
            relativeHeight, relativeWidth, size;

        if (viewBox && (width || height)) {
            viewBoxX = viewBox.x;
            viewBoxY = viewBox.y;
            viewBoxWidth = viewBox.width;
            viewBoxHeight = viewBox.height;
            relativeHeight = height / viewBoxHeight;
            relativeWidth = width / viewBoxWidth;
            size = Math.min(relativeWidth, relativeHeight);

            if (viewBoxWidth * size < width) {
                viewBoxX -= (width - viewBoxWidth * size) / 2 / size;
            }
            if (viewBoxHeight * size < height) {
                viewBoxY -= (height - viewBoxHeight * size) / 2 / size;
            }

            me.viewBoxShift = {
                dx: -viewBoxX,
                dy: -viewBoxY,
                scale: size
            };
            
            if (me.background) {
                me.background.setAttributes(Ext.apply({}, {
                    x: viewBoxX,
                    y: viewBoxY,
                    width: width / size,
                    height: height / size
                }, { hidden: false }), true);
            }
        } else {
            if (me.background && width && height) {
                me.background.setAttributes(Ext.apply({x: 0, y: 0, width: width, height: height}, { hidden: false }), true);
            }
        }
    },


    getBBox: function (sprite, isWithoutTransform) {
        var realPath = this["getPath" + sprite.type](sprite);
        if (isWithoutTransform) {
            sprite.bbox.plain = sprite.bbox.plain || Ext.draw.Draw.pathDimensions(realPath);
            return sprite.bbox.plain;
        }
        if (sprite.dirtyTransform) {
            this.applyTransformations(sprite, true);
        }
        sprite.bbox.transform = sprite.bbox.transform || Ext.draw.Draw.pathDimensions(Ext.draw.Draw.mapPath(realPath, sprite.matrix));
        return sprite.bbox.transform;
    },
    
    transformToViewBox: function (x, y) {
        if (this.viewBoxShift) {
            var me = this, shift = me.viewBoxShift;
            return [x / shift.scale - shift.dx, y / shift.scale - shift.dy];
        } else {
            return [x, y];
        }
    },

    
    applyTransformations: function(sprite, onlyMatrix) {
        if (sprite.type == 'text') {
            
            sprite.bbox.transform = 0;
            this.transform(sprite, false);
        }


        sprite.dirtyTransform = false;
        
        var me = this,
            attr = sprite.attr;

        if (attr.translation.x != null || attr.translation.y != null) {
            me.translate(sprite);
        }
        if (attr.scaling.x != null || attr.scaling.y != null) {
            me.scale(sprite);
        }
        if (attr.rotation.degrees != null) {
            me.rotate(sprite);
        }
        
        sprite.bbox.transform = 0;
        this.transform(sprite, onlyMatrix);
        sprite.transformations = [];
    },

    
    rotate: function (sprite) {
        var bbox,
            deg = sprite.attr.rotation.degrees,
            centerX = sprite.attr.rotation.x,
            centerY = sprite.attr.rotation.y;
        if (!Ext.isNumber(centerX) || !Ext.isNumber(centerY)) {
            bbox = this.getBBox(sprite, true);
            centerX = !Ext.isNumber(centerX) ? bbox.x + bbox.width / 2 : centerX;
            centerY = !Ext.isNumber(centerY) ? bbox.y + bbox.height / 2 : centerY;
        }
        sprite.transformations.push({
            type: "rotate",
            degrees: deg,
            x: centerX,
            y: centerY
        });
    },

    
    translate: function(sprite) {
        var x = sprite.attr.translation.x || 0,
            y = sprite.attr.translation.y || 0;
        sprite.transformations.push({
            type: "translate",
            x: x,
            y: y
        });
    },

    
    scale: function(sprite) {
        var bbox,
            x = sprite.attr.scaling.x || 1,
            y = sprite.attr.scaling.y || 1,
            centerX = sprite.attr.scaling.centerX,
            centerY = sprite.attr.scaling.centerY;

        if (!Ext.isNumber(centerX) || !Ext.isNumber(centerY)) {
            bbox = this.getBBox(sprite, true);
            centerX = !Ext.isNumber(centerX) ? bbox.x + bbox.width / 2 : centerX;
            centerY = !Ext.isNumber(centerY) ? bbox.y + bbox.height / 2 : centerY;
        }
        sprite.transformations.push({
            type: "scale",
            x: x,
            y: y,
            centerX: centerX,
            centerY: centerY
        });
    },

    
    rectPath: function (x, y, w, h, r) {
        if (r) {
            return [["M", x + r, y], ["l", w - r * 2, 0], ["a", r, r, 0, 0, 1, r, r], ["l", 0, h - r * 2], ["a", r, r, 0, 0, 1, -r, r], ["l", r * 2 - w, 0], ["a", r, r, 0, 0, 1, -r, -r], ["l", 0, r * 2 - h], ["a", r, r, 0, 0, 1, r, -r], ["z"]];
        }
        return [["M", x, y], ["l", w, 0], ["l", 0, h], ["l", -w, 0], ["z"]];
    },

    
    ellipsePath: function (x, y, rx, ry) {
        if (ry == null) {
            ry = rx;
        }
        return [["M", x, y], ["m", 0, -ry], ["a", rx, ry, 0, 1, 1, 0, 2 * ry], ["a", rx, ry, 0, 1, 1, 0, -2 * ry], ["z"]];
    },

    
    getPathpath: function (el) {
        return el.attr.path;
    },

    
    getPathcircle: function (el) {
        var a = el.attr;
        return this.ellipsePath(a.x, a.y, a.radius, a.radius);
    },

    
    getPathellipse: function (el) {
        var a = el.attr;
        return this.ellipsePath(a.x, a.y,
                                a.radiusX || (a.width / 2) || 0,
                                a.radiusY || (a.height / 2) || 0);
    },

    
    getPathrect: function (el) {
        var a = el.attr;
        return this.rectPath(a.x || 0, a.y || 0, a.width || 0, a.height || 0, a.r || 0);
    },

    
    getPathimage: function (el) {
        var a = el.attr;
        return this.rectPath(a.x || 0, a.y || 0, a.width, a.height);
    },

    
    getPathtext: function (el) {
        var bbox = this.getBBoxText(el);
        return this.rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
    },

    createGroup: function(id) {
        var group = this.groups.get(id);
        if (!group) {
            group = new Ext.draw.CompositeSprite({
                surface: this
            });
            group.id = id || Ext.id(null, 'ext-surface-group-');
            this.groups.add(group);
        }
        return group;
    },

    
    getGroup: function(id) {
        var group;
        if (typeof id == "string") {
            group = this.groups.get(id);
            if (!group) {
                group = this.createGroup(id);
            }
        } else {
            group = id;
        }
        return group;
    },

    
    prepareItems: function(items, applyDefaults) {
        items = [].concat(items);
        
        var item, i, ln;
        for (i = 0, ln = items.length; i < ln; i++) {
            item = items[i];
            if (!(item instanceof Ext.draw.Sprite)) {
                
                item.surface = this;
                items[i] = this.createItem(item);
            } else {
                item.surface = this;
            }
        }
        return items;
    },

    
    setText: Ext.emptyFn,

    
    
    createItem: Ext.emptyFn,

    
    getId: function() {
        return this.id || (this.id = Ext.id(null, 'ext-surface-'));
    },

    
    destroy: function() {
        var me = this;
        delete me.domRef;
        if (me.background) {
            me.background.destroy();
        }
        me.removeAll(true);
        Ext.destroy(me.groups.items);
    }
});



Ext.define('Ext.layout.component.Draw', {

    

    alias: 'layout.draw',

    extend:  Ext.layout.component.Auto ,

    setHeightInDom: true,

    setWidthInDom: true,

    

    type: 'draw',
    
    measureContentWidth : function (ownerContext) {
        var target = ownerContext.target,
            paddingInfo = ownerContext.getPaddingInfo(),
            bbox = this.getBBox(ownerContext);
            
        if (!target.viewBox) {
            if (target.autoSize) {
                return bbox.width + paddingInfo.width;
            } else {
                return bbox.x + bbox.width + paddingInfo.width;
            }
        } else {
            if (ownerContext.heightModel.shrinkWrap) {
                return paddingInfo.width;
            } else {
                return bbox.width / bbox.height * (ownerContext.getProp('contentHeight') - paddingInfo.height) + paddingInfo.width;
            }
        }
    },
    
    measureContentHeight : function (ownerContext) {
        var target = ownerContext.target,
            paddingInfo = ownerContext.getPaddingInfo(),
            bbox = this.getBBox(ownerContext);
            
        if (!ownerContext.target.viewBox) {
            if (target.autoSize) {
                return bbox.height + paddingInfo.height;
            } else {
                return bbox.y + bbox.height + paddingInfo.height;
            }
        } else {
            if (ownerContext.widthModel.shrinkWrap) {
                return paddingInfo.height;
            } else {
                return bbox.height / bbox.width * (ownerContext.getProp('contentWidth') - paddingInfo.width) + paddingInfo.height;
            }
        }
    },
    
    getBBox: function(ownerContext) {
        var bbox = ownerContext.surfaceBBox;
        if (!bbox) {
            bbox = ownerContext.target.surface.items.getBBox();
            
            if (bbox.width === -Infinity && bbox.height === -Infinity) {
                bbox.width = bbox.height = bbox.x = bbox.y = 0;
            }
            ownerContext.surfaceBBox = bbox;
        }
        return bbox;
    },

    publishInnerWidth: function (ownerContext, width) {
        ownerContext.setContentWidth(width - ownerContext.getFrameInfo().width, true);
    },
    
    publishInnerHeight: function (ownerContext, height) {
        ownerContext.setContentHeight(height - ownerContext.getFrameInfo().height, true);
    },
    
    finishedLayout: function (ownerContext) {
        
        var props = ownerContext.props,
            paddingInfo = ownerContext.getPaddingInfo();

        
        
        this.owner.setSurfaceSize(props.contentWidth - paddingInfo.width, props.contentHeight - paddingInfo.height);
        
        
        this.callParent(arguments);
    }
});


Ext.define('Ext.draw.Component', {

    

    alias: 'widget.draw',

    extend:  Ext.Component ,

               
                           
                                   
      

    

    
    enginePriority: ['Svg', 'Vml'],

    baseCls: Ext.baseCSSPrefix + 'surface',

    componentLayout: 'draw',

    
    viewBox: true,

    shrinkWrap: 3,
    
    
    autoSize: false,

    

    

    
    
    suspendSizing: 0,

    initComponent: function() {
        this.callParent(arguments);

        this.addEvents(
            
            'mousedown',
            
            'mouseup',
            
            'mousemove',
            
            'mouseenter',
            
            'mouseleave',
            
            'click',
            
            'dblclick'
        );
    },

    
    onRender: function() {
        this.callParent(arguments);
        if (this.createSurface() !== false) {
            this.configureSurfaceSize();
        }
    },
    
    configureSurfaceSize: function(){
        var me = this,
            viewBox = me.viewBox,
            autoSize = me.autoSize,
            bbox;

        if ((viewBox || autoSize) && !me.suspendSizing) {
            bbox = me.surface.items.getBBox();
            if (viewBox) {
                me.surface.setViewBox(bbox.x, bbox.y, bbox.width, bbox.height);
            } else {
                me.autoSizeSurface(bbox);
            }
        }
    },

    
    autoSizeSurface: function(bbox) {
        bbox = bbox || this.surface.items.getBBox();
        this.setSurfaceSize(bbox.width, bbox.height);
    },

    setSurfaceSize: function (width, height) {
        this.surface.setSize(width, height);
        if (this.autoSize) {
            var bbox = this.surface.items.getBBox();
            this.surface.setViewBox(bbox.x, bbox.y - (+Ext.isOpera), width, height);
        }
    },
    
    
    createSurface: function() {
        var me = this,
            cfg = Ext.applyIf({
                renderTo: me.el,
                height: me.height,
                width: me.width,
                items: me.items
            }, me.initialConfig), surface;

        
        delete cfg.listeners;
        if (!cfg.gradients) {
            cfg.gradients = me.gradients;
        }
        me.initSurfaceCfg(cfg);
        surface = Ext.draw.Surface.create(cfg, me.enginePriority);
        if (!surface) {
            
            return false;
        }
        me.surface = surface;
        surface.owner = me;


        function refire(eventName) {
            return function(e) {
                me.fireEvent(eventName, e);
            };
        }

        surface.on({
            scope: me,
            mouseup: refire('mouseup'),
            mousedown: refire('mousedown'),
            mousemove: refire('mousemove'),
            mouseenter: refire('mouseenter'),
            mouseleave: refire('mouseleave'),
            click: refire('click'),
            dblclick: refire('dblclick')
        });
    },
    
    initSurfaceCfg: Ext.emptyFn,


    
    onDestroy: function() {
        Ext.destroy(this.surface);
        this.callParent(arguments);
    }

});


Ext.chart = Ext.chart || {};

Ext.define('Ext.chart.theme.Theme', (


function() {
   

(function() {
    Ext.chart.theme = function(config, base) {
        config = config || {};
        var i = 0, d = Ext.Date.now(), l, colors, color,
            seriesThemes, markerThemes,
            seriesTheme, markerTheme, 
            key, gradients = [],
            midColor, midL;
        
        if (config.baseColor) {
            midColor = Ext.draw.Color.fromString(config.baseColor);
            midL = midColor.getHSL()[2];
            if (midL < 0.15) {
                midColor = midColor.getLighter(0.3);
            } else if (midL < 0.3) {
                midColor = midColor.getLighter(0.15);
            } else if (midL > 0.85) {
                midColor = midColor.getDarker(0.3);
            } else if (midL > 0.7) {
                midColor = midColor.getDarker(0.15);
            }
            config.colors = [ midColor.getDarker(0.3).toString(),
                              midColor.getDarker(0.15).toString(),
                              midColor.toString(),
                              midColor.getLighter(0.15).toString(),
                              midColor.getLighter(0.3).toString()];

            delete config.baseColor;
        }
        if (config.colors) {
            colors = config.colors.slice();
            markerThemes = base.markerThemes;
            seriesThemes = base.seriesThemes;
            l = colors.length;
            base.colors = colors;
            for (; i < l; i++) {
                color = colors[i];
                markerTheme = markerThemes[i] || {};
                seriesTheme = seriesThemes[i] || {};
                markerTheme.fill = seriesTheme.fill = markerTheme.stroke = seriesTheme.stroke = color;
                markerThemes[i] = markerTheme;
                seriesThemes[i] = seriesTheme;
            }
            base.markerThemes = markerThemes.slice(0, l);
            base.seriesThemes = seriesThemes.slice(0, l);
        
        }
        for (key in base) {
            if (key in config) {
                if (Ext.isObject(config[key]) && Ext.isObject(base[key])) {
                    Ext.apply(base[key], config[key]);
                } else {
                    base[key] = config[key];
                }
            }
        }
        if (config.useGradients) {
            colors = base.colors || (function () {
                var ans = [];
                for (i = 0, seriesThemes = base.seriesThemes, l = seriesThemes.length; i < l; i++) {
                    ans.push(seriesThemes[i].fill || seriesThemes[i].stroke);
                }
                return ans;
            }());
            for (i = 0, l = colors.length; i < l; i++) {
                midColor = Ext.draw.Color.fromString(colors[i]);
                if (midColor) {
                    color = midColor.getDarker(0.1).toString();
                    midColor = midColor.toString();
                    key = 'theme-' + midColor.substr(1) + '-' + color.substr(1) + '-' + d;
                    gradients.push({
                        id: key,
                        angle: 45,
                        stops: {
                            0: {
                                color: midColor.toString()
                            },
                            100: {
                                color: color.toString()
                            }
                        }
                    });
                    colors[i] = 'url(#' + key + ')'; 
                }
            }
            base.gradients = gradients;
            base.colors = colors;
        }
        
        Ext.apply(this, base);
    };
}());

return {

    

                                 

    

    theme: 'Base',
    themeAttrs: false,

    initTheme: function(theme) {
        var me = this,
            themes = Ext.chart.theme,
            key, gradients;
        if (theme) {
            theme = theme.split(':');
            for (key in themes) {
                if (key == theme[0]) {
                    gradients = theme[1] == 'gradients';
                    me.themeAttrs = new themes[key]({
                        useGradients: gradients
                    });
                    if (gradients) {
                        me.gradients = me.themeAttrs.gradients;
                    }
                    if (me.themeAttrs.background) {
                        me.background = me.themeAttrs.background;
                    }
                    return;
                }
            }
        }
    }
};

})());


Ext.define('Ext.chart.MaskLayer', {
    extend:  Ext.Component ,
    
    constructor: function(config) {
        config = Ext.apply(config || {}, {
            style: 'position:absolute;background-color:#ff9;cursor:crosshair;opacity:0.5;border:1px solid #00f;'
        });
        this.callParent([config]);    
    },
    
    initComponent: function() {
        var me = this;
        me.callParent(arguments);
        me.addEvents(
            'mousedown',
            'mouseup',
            'mousemove',
            'mouseenter',
            'mouseleave'
        );
    },

    initDraggable: function() {
        this.callParent(arguments);
        this.dd.onStart = function (e) {
            var me = this,
                comp = me.comp;
    
            
            this.startPosition = comp.getPosition(true);
    
            
            
            if (comp.ghost && !comp.liveDrag) {
                 me.proxy = comp.ghost();
                 me.dragTarget = me.proxy.header.el;
            }
    
            
            if (me.constrain || me.constrainDelegate) {
                me.constrainTo = me.calculateConstrainRegion();
            }
        };
    }
});


Ext.define('Ext.chart.Mask', {
               
                             
      
    
    

    
    constructor: function(config) {
        var me = this;

        me.addEvents('select');

        if (config) {
            Ext.apply(me, config);
        }
        if (me.enableMask) {
            me.on('afterrender', function() {
                
                var comp = new Ext.chart.MaskLayer({
                    renderTo: me.el,
                    hidden: true
                });
                comp.el.on({
                    'mousemove': function(e) {
                        me.onMouseMove(e);
                    },
                    'mouseup': function(e) {
                        me.onMouseUp(e);
                    }
                });
                comp.initDraggable();
                me.maskType = me.mask;
                me.mask = comp;
                me.maskSprite = me.surface.add({
                    type: 'path',
                    path: ['M', 0, 0],
                    zIndex: 1001,
                    opacity: 0.6,
                    hidden: true,
                    stroke: '#00f',
                    cursor: 'crosshair'
                });
            }, me, { single: true });
        }
    },

    onMouseUp: function(e) {
        var me = this,
            bbox = me.bbox || me.chartBBox,
            sel;
        me.maskMouseDown = false;
        me.mouseDown = false;
        if (me.mouseMoved) {
            me.handleMouseEvent(e);
            me.mouseMoved = false;
            sel = me.maskSelection;
            me.fireEvent('select', me, {
                x: sel.x - bbox.x,
                y: sel.y - bbox.y,
                width: sel.width,
                height: sel.height
            });
        }
    },

    onMouseDown: function(e) {
        this.handleMouseEvent(e);
    },

    onMouseMove: function(e) {
        this.handleMouseEvent(e);
    },

    handleMouseEvent: function(e) {
        var me = this,
            mask = me.maskType,
            bbox = me.bbox || me.chartBBox,
            x = bbox.x,
            y = bbox.y,
            math = Math,
            floor = math.floor,
            abs = math.abs,
            min = math.min,
            max = math.max,
            height = floor(y + bbox.height),
            width = floor(x + bbox.width),
            staticX = e.getPageX() - me.el.getX(),
            staticY = e.getPageY() - me.el.getY(),
            maskMouseDown = me.maskMouseDown,
            path;

        staticX = max(staticX, x);
        staticY = max(staticY, y);
        staticX = min(staticX, width);
        staticY = min(staticY, height);

        if (e.type === 'mousedown') {
            
            me.mouseDown = true;
            me.mouseMoved = false;
            me.maskMouseDown = {
                x: staticX,
                y: staticY
            };
        }
        else {
            
            
            me.mouseMoved = me.mouseDown;
            if (maskMouseDown && me.mouseDown) {
                if (mask == 'horizontal') {
                    staticY = y;
                    maskMouseDown.y = height;
                }
                else if (mask == 'vertical') {
                    staticX = x;
                    maskMouseDown.x = width;
                }
                width = maskMouseDown.x - staticX;
                height = maskMouseDown.y - staticY;
                path = ['M', staticX, staticY, 'l', width, 0, 0, height, -width, 0, 'z'];
                me.maskSelection = {
                    x: (width > 0 ? staticX : staticX + width) + me.el.getX(),
                    y: (height > 0 ? staticY : staticY + height) + me.el.getY(),
                    width: abs(width),
                    height: abs(height)
                };
                me.mask.updateBox(me.maskSelection);
                me.mask.show();
                me.maskSprite.setAttributes({
                    hidden: true    
                }, true);
            }
            else {
                if (mask == 'horizontal') {
                    path = ['M', staticX, y, 'L', staticX, height];
                }
                else if (mask == 'vertical') {
                    path = ['M', x, staticY, 'L', width, staticY];
                }
                else {
                    path = ['M', staticX, y, 'L', staticX, height, 'M', x, staticY, 'L', width, staticY];
                }
                me.maskSprite.setAttributes({
                    path: path,
                    'stroke-width': mask === true ? 1 : 1,
                    hidden: false
                }, true);
            }
        }

    },

    onMouseLeave: function(e) {
        var me = this;
        me.mouseMoved = false;
        me.mouseDown = false;
        me.maskMouseDown = false;
        me.mask.hide();
        me.maskSprite.hide(true);
    }
});
    


Ext.define('Ext.chart.Navigation', {

    
    setZoom: function(zoomConfig) {
        var me = this,
            axesItems = me.axes.items,
            i, ln, axis,
            bbox = me.chartBBox,
            xScale = bbox.width,
            yScale = bbox.height,
            zoomArea = {
                x : zoomConfig.x - me.el.getX(),
                y : zoomConfig.y - me.el.getY(),
                width : zoomConfig.width,
                height : zoomConfig.height
            },
            zoomer, ends, from, to, store, count, step, length, horizontal;

        for (i = 0, ln = axesItems.length; i < ln; i++) {
            axis = axesItems[i];
            horizontal = (axis.position == 'bottom' || axis.position == 'top');
            if (axis.type == 'Category') {
                if (!store) {
                    store = me.getChartStore();
                    count = store.data.items.length;
                }
                zoomer = zoomArea;
                length = axis.length;
                step = Math.round(length / count);
                if (horizontal) {
                    from = (zoomer.x ? Math.floor(zoomer.x / step) + 1 : 0);
                    to = (zoomer.x + zoomer.width) / step;
                } else {
                    from = (zoomer.y ? Math.floor(zoomer.y / step) + 1 : 0);
                    to = (zoomer.y + zoomer.height) / step;
                }
            }
            else {
                zoomer = {
                    x : zoomArea.x / xScale,
                    y : zoomArea.y / yScale,
                    width : zoomArea.width / xScale,
                    height : zoomArea.height / yScale
                }
                ends = axis.calcEnds();
                if (horizontal) {
                    from = (ends.to - ends.from) * zoomer.x + ends.from;
                    to = (ends.to - ends.from) * zoomer.width + from;
                } else {
                    to = (ends.to - ends.from) * (1 - zoomer.y) + ends.from;
                    from = to - (ends.to - ends.from) * zoomer.height;
                }
            }
            axis.minimum = from;
            axis.maximum = to;
            if (horizontal) {
                if (axis.doConstrain && me.maskType != 'vertical') {
                    axis.doConstrain();
                }
            }
            else {
                if (axis.doConstrain && me.maskType != 'horizontal') {
                    axis.doConstrain();
                }
            }
        }
        me.redraw(false);
    },

    
    restoreZoom: function() {
        var me = this,
            axesItems = me.axes.items,
            i, ln, axis;

        me.setSubStore(null);
        for (i = 0, ln = axesItems.length; i < ln; i++) {
            axis = axesItems[i];
            delete axis.minimum;
            delete axis.maximum;
        }
        me.redraw(false);
    }

});


Ext.define('Ext.chart.Shape', {

    

    singleton: true,

    

    circle: function (surface, opts) {
        return surface.add(Ext.apply({
            type: 'circle',
            x: opts.x,
            y: opts.y,
            stroke: null,
            radius: opts.radius
        }, opts));
    },
    line: function (surface, opts) {
        return surface.add(Ext.apply({
            type: 'rect',
            x: opts.x - opts.radius,
            y: opts.y - opts.radius,
            height: 2 * opts.radius,
            width: 2 * opts.radius / 5
        }, opts));
    },
    square: function (surface, opts) {
        return surface.add(Ext.applyIf({
            type: 'rect',
            x: opts.x - opts.radius,
            y: opts.y - opts.radius,
            height: 2 * opts.radius,
            width: 2 * opts.radius,
            radius: null
        }, opts));
    },
    triangle: function (surface, opts) {
        opts.radius *= 1.75;
        return surface.add(Ext.apply({
            type: 'path',
            stroke: null,
            path: "M".concat(opts.x, ",", opts.y, "m0-", opts.radius * 0.58, "l", opts.radius * 0.5, ",", opts.radius * 0.87, "-", opts.radius, ",0z")
        }, opts));
    },
    diamond: function (surface, opts) {
        var r = opts.radius;
        r *= 1.5;
        return surface.add(Ext.apply({
            type: 'path',
            stroke: null,
            path: ["M", opts.x, opts.y - r, "l", r, r, -r, r, -r, -r, r, -r, "z"]
        }, opts));
    },
    cross: function (surface, opts) {
        var r = opts.radius;
        r = r / 1.7;
        return surface.add(Ext.apply({
            type: 'path',
            stroke: null,
            path: "M".concat(opts.x - r, ",", opts.y, "l", [-r, -r, r, -r, r, r, r, -r, r, r, -r, r, r, r, -r, r, -r, -r, -r, r, -r, -r, "z"])
        }, opts));
    },
    plus: function (surface, opts) {
        var r = opts.radius / 1.3;
        return surface.add(Ext.apply({
            type: 'path',
            stroke: null,
            path: "M".concat(opts.x - r / 2, ",", opts.y - r / 2, "l", [0, -r, r, 0, 0, r, r, 0, 0, r, -r, 0, 0, r, -r, 0, 0, -r, -r, 0, 0, -r, "z"])
        }, opts));
    },
    arrow: function (surface, opts) {
        var r = opts.radius;
        return surface.add(Ext.apply({
            type: 'path',
            path: "M".concat(opts.x - r * 0.7, ",", opts.y - r * 0.4, "l", [r * 0.6, 0, 0, -r * 0.4, r, r * 0.8, -r, r * 0.8, 0, -r * 0.4, -r * 0.6, 0], "z")
        }, opts));
    },
    drop: function (surface, x, y, text, size, angle) {
        size = size || 30;
        angle = angle || 0;
        surface.add({
            type: 'path',
            path: ['M', x, y, 'l', size, 0, 'A', size * 0.4, size * 0.4, 0, 1, 0, x + size * 0.7, y - size * 0.7, 'z'],
            fill: '#000',
            stroke: 'none',
            rotate: {
                degrees: 22.5 - angle,
                x: x,
                y: y
            }
        });
        angle = (angle + 90) * Math.PI / 180;
        surface.add({
            type: 'text',
            x: x + size * Math.sin(angle) - 10, 
            y: y + size * Math.cos(angle) + 5,
            text:  text,
            'font-size': size * 12 / 40,
            stroke: 'none',
            fill: '#fff'
        });
    }
});


Ext.define('Ext.chart.LegendItem', {

    

    extend:  Ext.draw.CompositeSprite ,

                                  

    

    
    hiddenSeries: false,
    
    
    label: undefined,
    
    
    x: 0,
    y: 0,
    zIndex: 500,

    
    boldRe: /bold\s\d{1,}.*/i,

    constructor: function(config) {
        this.callParent(arguments);
        this.createLegend(config);
    },

    
    createLegend: function(config) {
        var me = this,
            series = me.series,
            index = config.yFieldIndex;
            
        me.label = me.createLabel(config);
        me.createSeriesMarkers(config);
        
        me.setAttributes({
            hidden: false
        }, true);
        
        me.yFieldIndex = index;

        
        me.on('mouseover', me.onMouseOver, me);
        me.on('mouseout',  me.onMouseOut,  me);
        me.on('mousedown', me.onMouseDown, me);
        
        if (!series.visibleInLegend(index)) {
            me.hiddenSeries = true;
            me.label.setAttributes({
               opacity: 0.5
            }, true);
        };

        
        me.updatePosition({ x: 0, y: 0 }); 
    },
    
    
    getLabelText: function() {
        var me = this,
            series = me.series,
            idx = me.yFieldIndex;

        function getSeriesProp(name) {
            var val = series[name];
            return (Ext.isArray(val) ? val[idx] : val);
        }
        
        return getSeriesProp('title') || getSeriesProp('yField');
    },
    
    
    createLabel: function(config) {
        var me = this,
            legend = me.legend;
        
        return me.add('label', me.surface.add({
            type: 'text',
            x: 20,
            y: 0,
            zIndex: (me.zIndex || 0) + 2,
            fill: legend.labelColor,
            font: legend.labelFont,
            text: me.getLabelText(),
            style: {
                cursor: 'pointer'
            }
        }));
    },
    
    
    createSeriesMarkers: function(config) {
        var me = this,
            index = config.yFieldIndex,
            series = me.series,
            seriesType = series.type,
            surface = me.surface,
            z = me.zIndex;

        
        if (seriesType === 'line' || seriesType === 'scatter') {
            if(seriesType === 'line') {
                var seriesStyle = Ext.apply(series.seriesStyle, series.style);
                me.drawLine(0.5, 0.5, 16.5, 0.5, z, seriesStyle, index);
            };
            
            if (series.showMarkers || seriesType === 'scatter') {
                var markerConfig = Ext.apply(series.markerStyle, series.markerConfig || {}, {
                    fill: series.getLegendColor(index)
                });
                me.drawMarker(8.5, 0.5, z, markerConfig);
            }
        }
        
        else {
            me.drawFilledBox(12, 12, z, index);
        }
    },
    
    
    drawLine: function(fromX, fromY, toX, toY, z, seriesStyle, index) {
        var me = this,
            surface = me.surface,
            series = me.series;
        
        return me.add('line', surface.add({
            type: 'path',
            path: 'M' + fromX + ',' + fromY + 'L' + toX + ',' + toY,
            zIndex: (z || 0) + 2,
            "stroke-width": series.lineWidth,
            "stroke-linejoin": "round",
            "stroke-dasharray": series.dash,
            stroke: seriesStyle.stroke || series.getLegendColor(index) || '#000',
            style: {
                cursor: 'pointer'
            }
        }));
    },
    
    
    drawMarker: function(x, y, z, markerConfig) {
        var me = this,
            surface = me.surface,
            series = me.series;

        return me.add('marker', Ext.chart.Shape[markerConfig.type](surface, {
            fill: markerConfig.fill,
            x: x,
            y: y,
            zIndex: (z || 0) + 2,
            radius: markerConfig.radius || markerConfig.size,
            style: {
                cursor: 'pointer'
            }
        }));
    },
    
    
    drawFilledBox: function(width, height, z, index) {
        var me = this,
            surface = me.surface,
            series = me.series;
            
        return me.add('box', surface.add({
            type: 'rect',
            zIndex: (z || 0) + 2,
            x: 0,
            y: 0,
            width: width,
            height: height,
            fill: series.getLegendColor(index),
            style: {
                cursor: 'pointer'
            }
        }));
    },
    
    
    onMouseOver: function() {
        var me = this;
        
        me.label.setStyle({
            'font-weight': 'bold'
        });
        me.series._index = me.yFieldIndex;
        me.series.highlightItem();
    },
    
    
    onMouseOut: function() {
        var me = this,
            legend = me.legend,
            boldRe = me.boldRe;

        me.label.setStyle({
            'font-weight': legend.labelFont && boldRe.test(legend.labelFont) ? 'bold' : 'normal'
        });
        me.series._index = me.yFieldIndex;
        me.series.unHighlightItem();
    },
    
    
    onMouseDown: function() {
        var me = this,
            index = me.yFieldIndex;

        if (!me.hiddenSeries) {
            me.series.hideAll(index);
            me.label.setAttributes({
                opacity: 0.5
            }, true);
        } else {
            me.series.showAll(index);
            me.label.setAttributes({
                opacity: 1
            }, true);
        }
        me.hiddenSeries = !me.hiddenSeries; 
        me.legend.chart.redraw();
    },

    
    updatePosition: function(relativeTo) {
        var me = this,
            items = me.items,
            ln = items.length,
            currentX = me.x,
            currentY = me.y,
            item, i, x, y, translate, o,
            relativeX, relativeY;
            
        if (!relativeTo) {
            relativeTo = me.legend;
        }
        
        relativeX = relativeTo.x;
        relativeY = relativeTo.y;
        for (i = 0; i < ln; i++) {
            translate = true;
            item = items[i];
            switch (item.type) {
                case 'text':
                    x = 20 + relativeX + currentX;
                    y = relativeY + currentY;
                    translate = false;
                    break;
                case 'rect':
                    x = relativeX + currentX;
                    y = relativeY + currentY - 6;
                    break;
                default:
                    x = relativeX + currentX;
                    y = relativeY + currentY;
            }
            
            o = {
                x: x,
                y: y
            };
            item.setAttributes(translate ? {
                translate: o
            } : o, true);
        }
    }
});


Ext.define('Ext.chart.Legend', {

    

                                       

    

    
    visible: true,
    
    
    update: true,

    
    position: 'bottom',

    
    x: 0,

    
    y: 0,

    
    labelColor: '#000',

    
    labelFont: '12px Helvetica, sans-serif',

    
    boxStroke: '#000',

    
    boxStrokeWidth: 1,

    
    boxFill: '#FFF',

    
    itemSpacing: 10,

    
    padding: 5,

    
    width: 0,
    
    height: 0,

    
    boxZIndex: 100,

    
    constructor: function(config) {
        var me = this;
        if (config) {
            Ext.apply(me, config);
        }
        me.items = [];
        
        me.isVertical = ("left|right|float".indexOf(me.position) !== -1);

        
        me.origX = me.x;
        me.origY = me.y;
    },

    
    create: function() {
        var me = this,
            seriesItems = me.chart.series.items,
            i, ln, series;

        me.createBox();
        
        if (me.rebuild !== false) {
            me.createItems();
        }
        
        if (!me.created && me.isDisplayed()) {
            me.created = true;

            
            for (i = 0, ln = seriesItems.length; i < ln; i++) {
                series = seriesItems[i];
                series.on('titlechange', me.redraw, me);
            }
        }
    },
    
    init: Ext.emptyFn,

    
    redraw: function() {
        var me = this;
        
        me.create();
        me.updatePosition();
    },

    
    isDisplayed: function() {
        return this.visible && this.chart.series.findIndex('showInLegend', true) !== -1;
    },

    
    createItems: function() {
        var me = this,
            seriesItems = me.chart.series.items,
            items = me.items,
            fields, i, li, j, lj, series, item;

        
        me.removeItems();
        
        
        for (i = 0, li = seriesItems.length; i < li; i++) {
            series = seriesItems[i];
            
            if (series.showInLegend) {
                fields = [].concat(series.yField);
                
                for (j = 0, lj = fields.length; j < lj; j++) {
                    item = me.createLegendItem(series, j);
                    items.push(item);
                }
            }
        }
        
        me.alignItems();
    },
    
    
    removeItems: function() {
        var me = this,
            items = me.items,
            len = items ? items.length : 0,
            i;

        if (len) {
            for (i = 0; i < len; i++) {
                items[i].destroy();
            }
        }
        
        
        items.length = [];
    },
    
    
    alignItems: function() {
        var me = this,
            padding = me.padding,
            vertical = me.isVertical,
            mfloor = Math.floor,
            dim, maxWidth, maxHeight, totalWidth, totalHeight;
        
        dim = me.updateItemDimensions();

        maxWidth    = dim.maxWidth;
        maxHeight   = dim.maxHeight;
        totalWidth  = dim.totalWidth;
        totalHeight = dim.totalHeight;

        
        me.width = mfloor((vertical ? maxWidth : totalWidth) + padding * 2);
        me.height = mfloor((vertical ? totalHeight : maxHeight) + padding * 2);
    },
    
    updateItemDimensions: function() {
        var me = this,
            items = me.items,
            padding = me.padding,
            itemSpacing = me.itemSpacing,
            maxWidth = 0,
            maxHeight = 0,
            totalWidth = 0,
            totalHeight = 0,
            vertical = me.isVertical,
            mfloor = Math.floor,
            mmax = Math.max,
            spacing = 0,
            i, l, item, bbox, width, height;

        
        
        for (i = 0, l = items.length; i < l; i++) {
            item = items[i];
                
            bbox = item.getBBox();

            
            width  = bbox.width;
            height = bbox.height;

            spacing = (i === 0 ? 0 : itemSpacing);
            
            
            item.x = padding + mfloor(vertical ? 0 : totalWidth + spacing);
            item.y = padding + mfloor(vertical ? totalHeight + spacing : 0) + height / 2;

            
            totalWidth  += spacing + width;
            totalHeight += spacing + height;
            maxWidth     = mmax(maxWidth, width);
            maxHeight    = mmax(maxHeight, height);
        }

        return {
            totalWidth:  totalWidth,
            totalHeight: totalHeight,
            maxWidth:    maxWidth,
            maxHeight:   maxHeight
        };
    },
    
    
    createLegendItem: function(series, yFieldIndex) {
        var me = this;
        
        return new Ext.chart.LegendItem({
            legend: me,
            series: series,
            surface: me.chart.surface,
            yFieldIndex: yFieldIndex
        });
    },
    
    
    getBBox: function() {
        var me = this;
        return {
            x: Math.round(me.x) - me.boxStrokeWidth / 2,
            y: Math.round(me.y) - me.boxStrokeWidth / 2,
            width: me.width + me.boxStrokeWidth,
            height: me.height + me.boxStrokeWidth
        };
    },

    
    createBox: function() {
        var me = this,
            box, bbox;

        if (me.boxSprite) {
            me.boxSprite.destroy();
        }

        bbox = me.getBBox();
        
        
        
        
        
        if (isNaN(bbox.width) || isNaN(bbox.height)) {
            me.boxSprite = false;
            return;
        }
        
        box = me.boxSprite = me.chart.surface.add(Ext.apply({
            type: 'rect',
            stroke: me.boxStroke,
            "stroke-width": me.boxStrokeWidth,
            fill: me.boxFill,
            zIndex: me.boxZIndex
        }, bbox));

        box.redraw();
    },

    
    calcPosition: function() {
        var me = this,
            x, y,
            legendWidth = me.width,
            legendHeight = me.height,
            chart = me.chart,
            chartBBox = chart.chartBBox,
            insets = chart.insetPadding,
            chartWidth = chartBBox.width - (insets * 2),
            chartHeight = chartBBox.height - (insets * 2),
            chartX = chartBBox.x + insets,
            chartY = chartBBox.y + insets,
            surface = chart.surface,
            mfloor = Math.floor;

        
        switch(me.position) {
            case "left":
                x = insets;
                y = mfloor(chartY + chartHeight / 2 - legendHeight / 2);
                break;
            case "right":
                x = mfloor(surface.width - legendWidth) - insets;
                y = mfloor(chartY + chartHeight / 2 - legendHeight / 2);
                break;
            case "top":
                x = mfloor(chartX + chartWidth / 2 - legendWidth / 2);
                y = insets;
                break;
            case "bottom":
                x = mfloor(chartX + chartWidth / 2 - legendWidth / 2);
                y = mfloor(surface.height - legendHeight) - insets;
                break;
            default:
                x = mfloor(me.origX) + insets;
                y = mfloor(me.origY) + insets;
        }
        
        return { x: x, y: y };
    },
    
    
    updatePosition: function() {
        var me = this,
            items = me.items,
            pos, i, l, bbox;

        if (me.isDisplayed()) {
            
            pos = me.calcPosition();
            
            me.x = pos.x;
            me.y = pos.y;

            
            for (i = 0, l = items.length; i < l; i++) {
                items[i].updatePosition();
            }

            bbox = me.getBBox();

            
            
            
            
            
            if (isNaN(bbox.width) || isNaN(bbox.height)) {
                if (me.boxSprite) {
                    me.boxSprite.hide(true);
                }
            }
            else {
                if (!me.boxSprite) {
                    me.createBox();
                }
                
                
                me.boxSprite.setAttributes(bbox, true);
                me.boxSprite.show(true);
            }
        }
    },
    
    
    toggle: function(show) {
      var me = this,
          i = 0,
          items = me.items,
          len = items.length;

      if (me.boxSprite) {
          if (show) {
              me.boxSprite.show(true);
          } else {
              me.boxSprite.hide(true);
          }
      }

      for (; i < len; ++i) {
          if (show) {
            items[i].show(true);
          } else {
              items[i].hide(true);
          }
      }

      me.visible = show;
    }
});


Ext.define('Ext.chart.theme.Base', {

    

                                        

    

    constructor: function(config) {
        var ident = Ext.identityFn;
        Ext.chart.theme.call(this, config, {
            background: false,
            axis: {
                stroke: '#444',
                'stroke-width': 1
            },
            axisLabelTop: {
                fill: '#444',
                font: '12px Arial, Helvetica, sans-serif',
                spacing: 2,
                padding: 5,
                renderer: ident
            },
            axisLabelRight: {
                fill: '#444',
                font: '12px Arial, Helvetica, sans-serif',
                spacing: 2,
                padding: 5,
                renderer: ident
            },
            axisLabelBottom: {
                fill: '#444',
                font: '12px Arial, Helvetica, sans-serif',
                spacing: 2,
                padding: 5,
                renderer: ident
            },
            axisLabelLeft: {
                fill: '#444',
                font: '12px Arial, Helvetica, sans-serif',
                spacing: 2,
                padding: 5,
                renderer: ident
            },
            axisTitleTop: {
                font: 'bold 18px Arial',
                fill: '#444'
            },
            axisTitleRight: {
                font: 'bold 18px Arial',
                fill: '#444',
                rotate: {
                    x:0, y:0,
                    degrees: 270
                }
            },
            axisTitleBottom: {
                font: 'bold 18px Arial',
                fill: '#444'
            },
            axisTitleLeft: {
                font: 'bold 18px Arial',
                fill: '#444',
                rotate: {
                    x:0, y:0,
                    degrees: 270
                }
            },
            series: {
                'stroke-width': 0
            },
            seriesLabel: {
                font: '12px Arial',
                fill: '#333'
            },
            marker: {
                stroke: '#555',
                radius: 3,
                size: 3
            },
            colors: [ "#94ae0a", "#115fa6","#a61120", "#ff8809", "#ffd13e", "#a61187", "#24ad9a", "#7c7474", "#a66111"],
            seriesThemes: [{
                fill: "#115fa6"
            }, {
                fill: "#94ae0a"
            }, {
                fill: "#a61120"
            }, {
                fill: "#ff8809"
            }, {
                fill: "#ffd13e"
            }, {
                fill: "#a61187"
            }, {
                fill: "#24ad9a"
            }, {
                fill: "#7c7474"
            }, {
                fill: "#115fa6"
            }, {
                fill: "#94ae0a"
            }, {
                fill: "#a61120"
            }, {
                fill: "#ff8809"
            }, {
                fill: "#ffd13e"
            }, {
                fill: "#a61187"
            }, {
                fill: "#24ad9a"
            }, {
                fill: "#7c7474"
            }, {
                fill: "#a66111"
            }],
            markerThemes: [{
                fill: "#115fa6",
                type: 'circle' 
            }, {
                fill: "#94ae0a",
                type: 'cross'
            }, {
                fill: "#115fa6",
                type: 'plus' 
            }, {
                fill: "#94ae0a",
                type: 'circle'
            }, {
                fill: "#a61120",
                type: 'cross'
            }]
        });
    }
}, function() {
    var palette = ['#b1da5a', '#4ce0e7', '#e84b67', '#da5abd', '#4d7fe6', '#fec935'],
        names = ['Green', 'Sky', 'Red', 'Purple', 'Blue', 'Yellow'],
        i = 0, j = 0, l = palette.length, themes = Ext.chart.theme,
        categories = [['#f0a50a', '#c20024', '#2044ba', '#810065', '#7eae29'],
                      ['#6d9824', '#87146e', '#2a9196', '#d39006', '#1e40ac'],
                      ['#fbbc29', '#ce2e4e', '#7e0062', '#158b90', '#57880e'],
                      ['#ef5773', '#fcbd2a', '#4f770d', '#1d3eaa', '#9b001f'],
                      ['#7eae29', '#fdbe2a', '#910019', '#27b4bc', '#d74dbc'],
                      ['#44dce1', '#0b2592', '#996e05', '#7fb325', '#b821a1']],
        cats = categories.length;
    
    
    for (; i < l; i++) {
        themes[names[i]] = (function(color) {
            return Ext.extend(themes.Base, {
                constructor: function(config) {
                    themes.Base.prototype.constructor.call(this, Ext.apply({
                        baseColor: color
                    }, config));
                }
            });
        }(palette[i]));
    }
    
    
    for (i = 0; i < cats; i++) {
        themes['Category' + (i + 1)] = (function(category) {
            return Ext.extend(themes.Base, {
                constructor: function(config) {
                    themes.Base.prototype.constructor.call(this, Ext.apply({
                        colors: category
                    }, config));
                }
            });
        }(categories[i]));
    }
});


Ext.define('Ext.chart.Chart', {
    extend:  Ext.draw.Component ,

    alias: 'widget.chart',
    
    mixins: {
        themeManager:  Ext.chart.theme.Theme ,
        mask:  Ext.chart.Mask ,
        navigation:  Ext.chart.Navigation ,
        bindable:  Ext.util.Bindable ,
        observable:  Ext.util.Observable 
    },

           
                                 
      
    
               
                                   
                                
                           
                               
                                
                              
      

    

    
    viewBox: false,

    

    
    animate: false,

    
    legend: false,

    
    insetPadding: 10,

    
    background: false,

    

    

    

    
    
    refreshBuffer: 1,

    constructor: function(config) {
        var me = this,
            defaultAnim;

        config = Ext.apply({}, config);
        me.initTheme(config.theme || me.theme);
        if (me.gradients) {
            Ext.apply(config, { gradients: me.gradients });
        }
        if (me.background) {
            Ext.apply(config, { background: me.background });
        }
        if (config.animate) {
            defaultAnim = {
                easing: 'ease',
                duration: 500
            };
            if (Ext.isObject(config.animate)) {
                config.animate = Ext.applyIf(config.animate, defaultAnim);
            }
            else {
                config.animate = defaultAnim;
            }
        }

        me.mixins.observable.constructor.call(me, config);
        
        if (config.mask) {
            config = Ext.apply({ enableMask: true }, config);
        }
        
        if (config.enableMask) {
            me.mixins.mask.constructor.call(me, config);
        }
        me.mixins.navigation.constructor.call(me);
        me.callParent([config]);
    },
    
    getChartStore: function(){
        return this.substore || this.store;
    },

    initComponent: function() {
        var me = this,
            axes,
            series;
        me.callParent();
        me.addEvents(
            'itemmousedown',
            'itemmouseup',
            'itemmouseover',
            'itemmouseout',
            'itemclick',
            'itemdblclick',
            'itemdragstart',
            'itemdrag',
            'itemdragend',
            
            'beforerefresh',
            
            'refresh'
        );
        Ext.applyIf(me, {
            zoom: {
                width: 1,
                height: 1,
                x: 0,
                y: 0
            }
        });
        me.maxGutters = { left: 0, right: 0, bottom: 0, top: 0 };
        me.store = Ext.data.StoreManager.lookup(me.store);
        axes = me.axes;
        me.axes = new Ext.util.MixedCollection(false, function(a) { return a.position; });
        if (axes) {
            me.axes.addAll(axes);
        }
        series = me.series;
        me.series = new Ext.util.MixedCollection(false, function(a) { return a.seriesId || (a.seriesId = Ext.id(null, 'ext-chart-series-')); });
        if (series) {
            me.series.addAll(series);
        }
        if (me.legend !== false) {
            me.legend = new Ext.chart.Legend(Ext.applyIf({
                chart: me
            }, me.legend));
        }

        me.on({
            mousemove: me.onMouseMove,
            mouseleave: me.onMouseLeave,
            mousedown: me.onMouseDown,
            mouseup: me.onMouseUp,
            click: me.onClick,
            dblclick: me.onDblClick,
            scope: me
        });
    },

    
    afterComponentLayout: function(width, height, oldWidth, oldHeight) {
        var me = this;
        if (Ext.isNumber(width) && Ext.isNumber(height)) {
            if (width !== oldWidth || height !== oldHeight) {
                me.curWidth = width;
                me.curHeight = height;
                me.redraw(true);
                me.needsRedraw = false;
            } else if (me.needsRedraw) {
                me.redraw();
                me.needsRedraw = false;
            }
        }
        this.callParent(arguments);
    },

    
    redraw: function(resize) {
        var me = this,
            seriesItems = me.series.items,
            seriesLen = seriesItems.length,
            axesItems = me.axes.items,
            axesLen = axesItems.length,
            themeIndex = 0,
            i, item,
            chartBBox = me.chartBBox = {
                x: 0,
                y: 0,
                height: me.curHeight,
                width: me.curWidth
            },
            legend = me.legend, 
            series;
            
        me.surface.setSize(chartBBox.width, chartBBox.height);
        
        for (i = 0; i < seriesLen; i++) {
            item = seriesItems[i];
            if (!item.initialized) {
                series = me.initializeSeries(item, i, themeIndex);
            } else {
                series = item;
            }
            
            
            series.onRedraw();
            
            
            if (Ext.isArray(item.yField)) {
                themeIndex += item.yField.length;
            } else {
                ++themeIndex;
            }
        }
        for (i = 0; i < axesLen; i++) {
            item = axesItems[i];
            if (!item.initialized) {
                me.initializeAxis(item);
            }
        }
        
        
        for (i = 0; i < axesLen; i++) {
            axesItems[i].processView();
        }
        for (i = 0; i < axesLen; i++) {
            axesItems[i].drawAxis(true);
        }

        
        if (legend !== false && legend.visible) {
            if (legend.update || !legend.created) {
                legend.create();
            }
        }

        
        me.alignAxes();

        
        if (legend !== false && legend.visible) {
            legend.updatePosition();
        }

        
        me.getMaxGutters();

        
        me.resizing = !!resize;

        for (i = 0; i < axesLen; i++) {
            axesItems[i].drawAxis();
        }
        for (i = 0; i < seriesLen; i++) {
            me.drawCharts(seriesItems[i]);
        }
        me.resizing = false;
    },

    
    afterRender: function() {
        var me = this,
            legend = me.legend;
        
        me.callParent(arguments);

        if (me.categoryNames) {
            me.setCategoryNames(me.categoryNames);
        }
        
        if (legend) {
            legend.init();
        }

        me.bindStore(me.store, true);
        me.refresh();

        if (me.surface.engine === 'Vml') {
            me.on('added', me.onAddedVml, me);
            me.mon(me.hierarchyEventSource, 'added', me.onContainerAddedVml, me);
        }
    },

    
    
    
    
    
    onAddedVml: function() {
        this.needsRedraw = true; 
    },

    onContainerAddedVml: function(container) {
        if (this.isDescendantOf(container)) {
            this.needsRedraw = true; 
        }
    },

    
    getEventXY: function(e) {
        var box = this.surface.getRegion(),
            pageXY = e.getXY(),
            x = pageXY[0] - box.left,
            y = pageXY[1] - box.top;
            
        return [x, y];
    },
    
    onClick: function(e) {
        this.handleClick('itemclick', e);
    },
    
    onDblClick: function(e) {
        this.handleClick('itemdblclick', e);
    },

    
    handleClick: function(name, e) {
        var me = this,
            position = me.getEventXY(e),
            seriesItems = me.series.items,
            i, ln, series,
            item;

        
        
        for (i = 0, ln = seriesItems.length; i < ln; i++) {
            series = seriesItems[i];
            if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) {
                if (series.getItemForPoint) {
                    item = series.getItemForPoint(position[0], position[1]);
                    if (item) {
                        series.fireEvent(name, item);
                    }
                }
            }
        }
    },

    
    onMouseDown: function(e) {
        var me = this,
            position = me.getEventXY(e),
            seriesItems = me.series.items,
            i, ln, series,
            item;

        if (me.enableMask) {
            me.mixins.mask.onMouseDown.call(me, e);
        }
        
        
        for (i = 0, ln = seriesItems.length; i < ln; i++) {
            series = seriesItems[i];
            if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) {
                if (series.getItemForPoint) {
                    item = series.getItemForPoint(position[0], position[1]);
                    if (item) {
                        series.fireEvent('itemmousedown', item);
                    }
                }
            }
        }
    },

    
    onMouseUp: function(e) {
        var me = this,
            position = me.getEventXY(e),
            seriesItems = me.series.items,
            i, ln, series,
            item;

        if (me.enableMask) {
            me.mixins.mask.onMouseUp.call(me, e);
        }
        
        
        for (i = 0, ln = seriesItems.length; i < ln; i++) {
            series = seriesItems[i];
            if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) {
                if (series.getItemForPoint) {
                    item = series.getItemForPoint(position[0], position[1]);
                    if (item) {
                        series.fireEvent('itemmouseup', item);
                    }
                }
            }
        }
    },

    
    onMouseMove: function(e) {
        var me = this,
            position = me.getEventXY(e),
            seriesItems = me.series.items,
            i, ln, series,
            item, last, storeItem, storeField;

        
        if (me.enableMask) {
            me.mixins.mask.onMouseMove.call(me, e);
        }
        
        
        for (i = 0, ln = seriesItems.length; i < ln; i++) {
            series = seriesItems[i];
            if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) {
                if (series.getItemForPoint) {
                    item = series.getItemForPoint(position[0], position[1]);
                    last = series._lastItemForPoint;
                    storeItem = series._lastStoreItem;
                    storeField = series._lastStoreField;


                    if (item !== last || item && (item.storeItem != storeItem || item.storeField != storeField)) {
                        if (last) {
                            series.fireEvent('itemmouseout', last);
                            delete series._lastItemForPoint;
                            delete series._lastStoreField;
                            delete series._lastStoreItem;
                        }
                        if (item) {
                            series.fireEvent('itemmouseover', item);
                            series._lastItemForPoint = item;
                            series._lastStoreItem = item.storeItem;
                            series._lastStoreField = item.storeField;
                        }
                    }
                }
            } else {
                last = series._lastItemForPoint;
                if (last) {
                    series.fireEvent('itemmouseout', last);
                    delete series._lastItemForPoint;
                    delete series._lastStoreField;
                    delete series._lastStoreItem;
                }
            }
        }
    },

    
    onMouseLeave: function(e) {
        var me = this,
            seriesItems = me.series.items,
            i, ln, series;

        if (me.enableMask) {
            me.mixins.mask.onMouseLeave.call(me, e);
        }
        for (i = 0, ln = seriesItems.length; i < ln; i++) {
            series = seriesItems[i];
            delete series._lastItemForPoint;
        }
    },

    
    delayRefresh: function() {
        var me = this;
        if (!me.refreshTask) {
            me.refreshTask = new Ext.util.DelayedTask(me.refresh, me);
        }
        me.refreshTask.delay(me.refreshBuffer);
    },

    
    refresh: function() {
        var me = this;
            
        if (me.rendered && me.curWidth !== undefined && me.curHeight !== undefined) {
            if (!me.isVisible(true)) {
                if (!me.refreshPending) {
                    me.setShowListeners('mon');
                    me.refreshPending = true;
                }
                return;
            }
            if (me.fireEvent('beforerefresh', me) !== false) {
                me.redraw();
                me.fireEvent('refresh', me);
            }
        }
    },
    
    onShow: function(){
        var me = this;
        me.callParent(arguments);
        if (me.refreshPending) {
            me.delayRefresh();
            me.setShowListeners('mun');
        }
        delete me.refreshPending;
    },
    
    setShowListeners: function(method){
        var me = this;
        me[method](me.hierarchyEventSource, {
            scope: me,
            single: true,
            show: me.forceRefresh,
            expand: me.forceRefresh
        });
    },
    
    doRefresh: function(){
        
        this.setSubStore(null);
        this.refresh();    
    },
    
    forceRefresh: function(container) {
        var me = this;
        if (me.isDescendantOf(container) && me.refreshPending) {
            
            
            me.setShowListeners('mun');
            me.delayRefresh();
        }    
        delete me.refreshPending;
    },

    bindStore: function(store, initial) {
        var me = this;
        me.mixins.bindable.bindStore.apply(me, arguments);
        if (me.store && !initial) {
            me.refresh();
        }
    },
    
    getStoreListeners: function() {
        var refresh = this.doRefresh,
            delayRefresh = this.delayRefresh;
            
        return {
            refresh: refresh,
            add: delayRefresh,
            bulkremove: delayRefresh,
            update: delayRefresh,
            clear: refresh
        };
    },
    
    setSubStore: function(subStore){
        this.substore = subStore;    
    },

    
    initializeAxis: function(axis) {
        var me = this,
            chartBBox = me.chartBBox,
            w = chartBBox.width,
            h = chartBBox.height,
            x = chartBBox.x,
            y = chartBBox.y,
            themeAttrs = me.themeAttrs,
            axes = me.axes,
            config = {
                chart: me
            };
            
        if (themeAttrs) {
            config.axisStyle = Ext.apply({}, themeAttrs.axis);
            config.axisLabelLeftStyle = Ext.apply({}, themeAttrs.axisLabelLeft);
            config.axisLabelRightStyle = Ext.apply({}, themeAttrs.axisLabelRight);
            config.axisLabelTopStyle = Ext.apply({}, themeAttrs.axisLabelTop);
            config.axisLabelBottomStyle = Ext.apply({}, themeAttrs.axisLabelBottom);
            config.axisTitleLeftStyle = Ext.apply({}, themeAttrs.axisTitleLeft);
            config.axisTitleRightStyle = Ext.apply({}, themeAttrs.axisTitleRight);
            config.axisTitleTopStyle = Ext.apply({}, themeAttrs.axisTitleTop);
            config.axisTitleBottomStyle = Ext.apply({}, themeAttrs.axisTitleBottom);
        }
        
        switch (axis.position) {
            case 'top':
                Ext.apply(config, {
                    length: w,
                    width: h,
                    x: x,
                    y: y
                });
            break;
            case 'bottom':
                Ext.apply(config, {
                    length: w,
                    width: h,
                    x: x,
                    y: h
                });
            break;
            case 'left':
                Ext.apply(config, {
                    length: h,
                    width: w,
                    x: x,
                    y: h
                });
            break;
            case 'right':
                Ext.apply(config, {
                    length: h,
                    width: w,
                    x: w,
                    y: h
                });
            break;
        }
        
        if (!axis.chart) {
            Ext.apply(config, axis);
            axis = Ext.createByAlias('axis.' + axis.type.toLowerCase(), config);
            axes.replace(axis);
        } else {
            Ext.apply(axis, config);
        }
        axis.initialized = true;
    },

    
    getInsets: function() {
        var me = this,
            insetPadding = me.insetPadding;

        return {
            top: insetPadding,
            right: insetPadding,
            bottom: insetPadding,
            left: insetPadding
        };
    },

    
    calculateInsets: function() {
        var me = this,
            legend = me.legend,
            axes = me.axes,
            edges = ['top', 'right', 'bottom', 'left'],
            insets, i, l, edge, isVertical, axis, bbox;

        function getAxis(edge) {
            var i = axes.findIndex('position', edge);
            return (i < 0) ? null : axes.getAt(i);
        }
        
        insets = me.getInsets();

        
        for (i = 0, l = edges.length; i < l; i++) {
            edge = edges[i];
            
            isVertical = (edge === 'left' || edge === 'right');
            axis = getAxis(edge);

            
            if (legend !== false) {
                if (legend.position === edge) {
                    bbox = legend.getBBox();
                    insets[edge] += (isVertical ? bbox.width : bbox.height) + me.insetPadding;
                }
            }

            
            
            if (axis && axis.bbox) {
                bbox = axis.bbox;
                insets[edge] += (isVertical ? bbox.width : bbox.height);
            }
        }
        
        return insets;
    },

    
    alignAxes: function() {
        var me = this,
            axesItems = me.axes.items,
            insets, chartBBox, i, l, axis, pos, isVertical;
        
        insets = me.calculateInsets();

        
        chartBBox = {
            x: insets.left,
            y: insets.top,
            width: me.curWidth - insets.left - insets.right,
            height: me.curHeight - insets.top - insets.bottom
        };
        me.chartBBox = chartBBox;

        
        
        for (i = 0, l = axesItems.length; i < l; i++) {
            axis = axesItems[i];
            pos = axis.position;
            isVertical = pos === 'left' || pos === 'right';

            axis.x = (pos === 'right' ? chartBBox.x + chartBBox.width : chartBBox.x);
            axis.y = (pos === 'top' ? chartBBox.y : chartBBox.y + chartBBox.height);
            axis.width = (isVertical ? chartBBox.width : chartBBox.height);
            axis.length = (isVertical ? chartBBox.height : chartBBox.width);
        }
    },

    
    initializeSeries: function(series, idx, themeIndex) {
        var me = this,
            themeAttrs = me.themeAttrs,
            seriesObj, markerObj, seriesThemes, st,
            markerThemes, colorArrayStyle = [],
            isInstance = (series instanceof Ext.chart.series.Series).
            i = 0, l, config;

        if (!series.initialized) {
            config = {
                chart: me,
                seriesId: series.seriesId
            };
            if (themeAttrs) {
                seriesThemes = themeAttrs.seriesThemes;
                markerThemes = themeAttrs.markerThemes;
                seriesObj = Ext.apply({}, themeAttrs.series);
                markerObj = Ext.apply({}, themeAttrs.marker);
                config.seriesStyle = Ext.apply(seriesObj, seriesThemes[themeIndex % seriesThemes.length]);
                config.seriesLabelStyle = Ext.apply({}, themeAttrs.seriesLabel);
                config.markerStyle = Ext.apply(markerObj, markerThemes[themeIndex % markerThemes.length]);
                if (themeAttrs.colors) {
                    config.colorArrayStyle = themeAttrs.colors;
                } else {
                    colorArrayStyle = [];
                    for (l = seriesThemes.length; i < l; i++) {
                        st = seriesThemes[i];
                        if (st.fill || st.stroke) {
                            colorArrayStyle.push(st.fill || st.stroke);
                        }
                    }
                    if (colorArrayStyle.length) {
                        config.colorArrayStyle = colorArrayStyle;
                    }
                }
                config.seriesIdx = idx;
                config.themeIdx = themeIndex;
            }
            
            if (isInstance) {
                Ext.applyIf(series, config);
            }
            else {
                Ext.applyIf(config, series);
                series = me.series.replace(Ext.createByAlias('series.' + series.type.toLowerCase(), config));
            }
        }

        series.initialize();
        series.initialized = true;
        return series;
    },

    
    getMaxGutters: function() {
        var me = this,
            seriesItems = me.series.items,
            i, ln, series, gutters,
            lowerH = 0, upperH = 0, lowerV = 0, upperV = 0;

        for (i = 0, ln = seriesItems.length; i < ln; i++) {
            gutters = seriesItems[i].getGutters();
            if (gutters) {
                if (gutters.verticalAxis) {
                    lowerV = Math.max(lowerV, gutters.lower);
                    upperV = Math.max(upperV, gutters.upper);
                }
                else {
                    lowerH = Math.max(lowerH, gutters.lower);
                    upperH = Math.max(upperH, gutters.upper);
                }
            }
        }
        me.maxGutters = {
            left: lowerH,
            right: upperH,
            bottom: lowerV,
            top: upperV
        };
    },

    
    drawAxis: function(axis) {
        axis.drawAxis();
    },

    
    drawCharts: function(series) {
        series.triggerafterrender = false;
        series.drawSeries();
        if (!this.animate) {
            series.fireEvent('afterrender', series);
        }
    },
    
    save: function(config){
        return Ext.draw.Surface.save(this.surface, config);
    },
    
    destroy: function() {
        var me = this,
            task = me.refreshTask;
        
        if (task) {
            task.cancel();
            me.refreshTask = null;
        }
        
        Ext.destroy(me.surface);
        me.bindStore(null);
        me.callParent(arguments);
    }
});


Ext.define('Ext.chart.Highlight', {

    

                              

    

    
    highlight: false,

    
    highlightCfg : {
        fill: '#fdd',
        "stroke-width": 5,
        stroke: '#f55'
    },

    constructor: function(config) {
        
        if (config.highlight && (typeof config.highlight !== 'boolean')) { 
            this.highlightCfg = Ext.merge({}, this.highlightCfg, config.highlight);
        }
    },

    
    highlightItem: function(item) {
        if (!item) {
            return;
        }
        
        var me = this,
            sprite = item.sprite,
            opts = Ext.merge({}, me.highlightCfg, me.highlight),
            surface = me.chart.surface,
            animate = me.chart.animate,
            p, from, to, pi;

        if (!me.highlight || !sprite || sprite._highlighted) {
            return;
        }
        if (sprite._anim) {
            sprite._anim.paused = true;
        }
        sprite._highlighted = true;
        if (!sprite._defaults) {
            sprite._defaults = Ext.apply({}, sprite.attr);
            from = {};
            to = {};
            
            for (p in opts) {
                if (! (p in sprite._defaults)) {
                    sprite._defaults[p] = surface.availableAttrs[p];
                }
                from[p] = sprite._defaults[p];
                to[p] = opts[p];
                if (Ext.isObject(opts[p])) {
                    from[p] = {};
                    to[p] = {};
                    Ext.apply(sprite._defaults[p], sprite.attr[p]);
                    Ext.apply(from[p], sprite._defaults[p]);
                    for (pi in sprite._defaults[p]) {
                        if (! (pi in opts[p])) {
                            to[p][pi] = from[p][pi];
                        } else {
                            to[p][pi] = opts[p][pi];
                        }
                    }
                    for (pi in opts[p]) {
                        if (! (pi in to[p])) {
                            to[p][pi] = opts[p][pi];
                        }
                    }
                }
            }
            sprite._from = from;
            sprite._to = to;
            sprite._endStyle = to;
        }
        if (animate) {
            sprite._anim = new Ext.fx.Anim({
                target: sprite,
                from: sprite._from,
                to: sprite._to,
                duration: 150
            });
        } else {
            sprite.setAttributes(sprite._to, true);
        }
    },

    
    unHighlightItem: function() {
        if (!this.highlight || !this.items) {
            return;
        }

        var me = this,
            items = me.items,
            len = items.length,
            opts = Ext.merge({}, me.highlightCfg, me.highlight),
            animate = me.chart.animate,
            i = 0,
            obj, p, sprite;
        for (; i < len; i++) {
            if (!items[i]) {
                continue;
            }
            sprite = items[i].sprite;
            if (sprite && sprite._highlighted) {
                if (sprite._anim) {
                    sprite._anim.paused = true;
                }
                obj = {};
                for (p in opts) {
                    if (Ext.isObject(sprite._defaults[p])) {
                        obj[p] = Ext.apply({}, sprite._defaults[p]);
                    }
                    else {
                        obj[p] = sprite._defaults[p];
                    }
                }
                if (animate) {
                    
                    sprite._endStyle = obj;
                    sprite._anim = new Ext.fx.Anim({
                        target: sprite,
                        to: obj,
                        duration: 150
                    });
                }
                else {
                    sprite.setAttributes(obj, true);
                }
                delete sprite._highlighted;
                
            }
        }
    },

    cleanHighlights: function() {
        if (!this.highlight) {
            return;
        }

        var group = this.group,
            markerGroup = this.markerGroup,
            i = 0,
            l;
        for (l = group.getCount(); i < l; i++) {
            delete group.getAt(i)._defaults;
        }
        if (markerGroup) {
            for (l = markerGroup.getCount(); i < l; i++) {
                delete markerGroup.getAt(i)._defaults;
            }
        }
    }
});


Ext.define('Ext.chart.Label', {

    

                                 

    

    
    
    
    
    

    
    colorStringRe: /url\s*\(\s*#([^\/)]+)\s*\)/,

    
    constructor: function(config) {
        var me = this;
        me.label = Ext.applyIf(me.label || {},
        {
            display: "none",
            stackedDisplay: "none",
            color: "#000",
            field: "name",
            minMargin: 50,
            font: "11px Helvetica, sans-serif",
            orientation: "horizontal",
            renderer: Ext.identityFn
        });

        if (me.label.display !== 'none') {
            me.labelsGroup = me.chart.surface.getGroup(me.seriesId + '-labels');
        }
    },

    
    renderLabels: function() {
        var me = this,
            chart = me.chart,
            gradients = chart.gradients,
            items = me.items,
            animate = chart.animate,
            config = me.label,
            display = config.display,
            stackedDisplay = config.stackedDisplay,
            format = config.renderer,
            color = config.color,
            field = [].concat(config.field),
            group = me.labelsGroup,
            groupLength = (group || 0) && group.length,
            store = me.chart.getChartStore(),
            len = store.getCount(),
            itemLength = (items || 0) && items.length,
            ratio = itemLength / len,
            gradientsCount = (gradients || 0) && gradients.length,
            Color = Ext.draw.Color,
            hides = [],
            gradient, i, count, groupIndex, index, j, k, colorStopTotal, colorStopIndex, colorStop, item, label,
            storeItem, sprite, spriteColor, spriteBrightness, labelColor, colorString,
            total, totalPositive, totalNegative, topText, bottomText;

        if (display == 'none' || !group) {
            return;
        }
        
        if(itemLength == 0){
            while(groupLength--) {
                hides.push(groupLength);
            }
        } else {
            for (i = 0, count = 0, groupIndex = 0; i < len; i++) {
                index = 0;
                for (j = 0; j < ratio; j++) {
                    item = items[count];
                    label = group.getAt(groupIndex);
                    storeItem = store.getAt(i);
                    
                    while(this.__excludes && this.__excludes[index]) {
                        index++;
                    }

                    if (!item && label) {
                        label.hide(true);
                        groupIndex++;
                    }

                    if (item && field[j]) {
                        if (!label) {
                            label = me.onCreateLabel(storeItem, item, i, display);
                            if (!label) {
                                break;
                            }
                        }

                        
                        label.setAttributes({
                            fill: String(color)
                        }, true);

                        
                        me.onPlaceLabel(label, storeItem, item, i, display, animate, index);
                        groupIndex++;

                        
                        if (config.contrast && item.sprite) {
                            sprite = item.sprite;
                            
                            
                            if (animate && sprite._endStyle) {
                                colorString = sprite._endStyle.fill;
                            } else if (animate && sprite._to) {
                                colorString = sprite._to.fill;
                            } else {
                                colorString = sprite.attr.fill;
                            }
                            colorString = colorString || sprite.attr.fill;

                            spriteColor = Color.fromString(colorString);
                            
                            if (colorString && !spriteColor) {
                                colorString = colorString.match(me.colorStringRe)[1];
                                for (k = 0; k < gradientsCount; k++) {
                                    gradient = gradients[k];
                                    if (gradient.id == colorString) {
                                        
                                        colorStop = 0; colorStopTotal = 0;
                                        for (colorStopIndex in gradient.stops) {
                                            colorStop++;
                                            colorStopTotal += Color.fromString(gradient.stops[colorStopIndex].color).getGrayscale();
                                        }
                                        spriteBrightness = (colorStopTotal / colorStop) / 255;
                                        break;
                                    }
                                }
                            }
                            else {
                                spriteBrightness = spriteColor.getGrayscale() / 255;
                            }
                            if (label.isOutside) {
                                spriteBrightness = 1;
                            }
                            labelColor = Color.fromString(label.attr.fill || label.attr.color).getHSL();
                            labelColor[2] = spriteBrightness > 0.5 ? 0.2 : 0.8;
                            label.setAttributes({
                                fill: String(Color.fromHSL.apply({}, labelColor))
                            }, true);
                        }

                        
                        if (me.stacked && stackedDisplay && (item.totalPositiveValues || item.totalNegativeValues)) {
                            totalPositive = (item.totalPositiveValues || 0);
                            totalNegative = (item.totalNegativeValues || 0);
                            total = totalPositive + totalNegative;

                            if (stackedDisplay == 'total') {
                                topText = format(total);
                            } else if (stackedDisplay == 'balances') {
                                if (totalPositive == 0 && totalNegative == 0) {
                                    topText = format(0);
                                } else {
                                    topText = format(totalPositive);
                                    bottomText = format(totalNegative);
                                }
                            }

                            if (topText) {
                                label = group.getAt(groupIndex);
                                if (!label) {
                                    label = me.onCreateLabel(storeItem, item, i, 'over');
                                }
                                labelColor = Color.fromString(label.attr.color || label.attr.fill).getHSL();
                                label.setAttributes({
                                    text: topText,
                                    style: config.font,
                                    fill: String(Color.fromHSL.apply({}, labelColor))
                                }, true);
                                me.onPlaceLabel(label, storeItem, item, i, 'over', animate, index);
                                groupIndex ++;
                            }

                            if (bottomText) {
                                label = group.getAt(groupIndex);
                                if (!label) {
                                    label = me.onCreateLabel(storeItem, item, i, 'under');
                                }
                                labelColor = Color.fromString(label.attr.color || label.attr.fill).getHSL();
                                label.setAttributes({
                                    text: bottomText,
                                    style: config.font,
                                    fill: String(Color.fromHSL.apply({}, labelColor))
                                }, true);
                                me.onPlaceLabel(label, storeItem, item, i, 'under', animate, index);
                                groupIndex ++;
                            }
                        }
                    }
                    count++;
                    index++;
                }
            }
            groupLength = group.length;
        
            while(groupLength > groupIndex){
                hides.push(groupIndex);
                groupIndex++;
           }
        }
        me.hideLabels(hides);
    },

    hideLabels: function(hides){
        var labelsGroup = this.labelsGroup,
            hlen = !!hides && hides.length;

        if (!labelsGroup) {
            return;
        }

        if (hlen === false) {
            hlen = labelsGroup.getCount();
            while (hlen--) {
              labelsGroup.getAt(hlen).hide(true);
            }
        } else {
            while(hlen--) {
                labelsGroup.getAt(hides[hlen]).hide(true);
            }
        }
    }
});


Ext.define('Ext.chart.TipSurface', {

    

    extend:  Ext.draw.Component ,

    

    spriteArray: false,
    renderFirst: true,

    constructor: function(config) {
        this.callParent([config]);
        if (config.sprites) {
            this.spriteArray = [].concat(config.sprites);
            delete config.sprites;
        }
    },

    onRender: function() {
        var me = this,
            i = 0,
            l = 0,
            sp,
            sprites;
            this.callParent(arguments);
        sprites = me.spriteArray;
        if (me.renderFirst && sprites) {
            me.renderFirst = false;
            for (l = sprites.length; i < l; i++) {
                sp = me.surface.add(sprites[i]);
                sp.setAttributes({
                    hidden: false
                },
                true);
            }
        }
    }
});


Ext.define('Ext.chart.Tip', {

    

                                                          

    

    constructor: function(config) {
        var me = this,
            surface,
            sprites,
            tipSurface;
        if (config.tips) {
            me.tipTimeout = null;
            me.tipConfig = Ext.apply({}, config.tips, {
                renderer: Ext.emptyFn,
                constrainPosition: true,
                autoHide: true,
                shrinkWrapDock: true
            });
            me.tooltip = new Ext.tip.ToolTip(me.tipConfig);
            me.chart.surface.on('mousemove', me.tooltip.onMouseMove, me.tooltip);
            me.chart.surface.on('mouseleave', function() {
                me.hideTip();
            });
            if (me.tipConfig.surface) {
                
                surface = me.tipConfig.surface;
                sprites = surface.sprites;
                tipSurface = new Ext.chart.TipSurface({
                    id: 'tipSurfaceComponent',
                    sprites: sprites
                });
                if (surface.width && surface.height) {
                    tipSurface.setSize(surface.width, surface.height);
                }
                me.tooltip.add(tipSurface);
                me.spriteTip = tipSurface;
            }
        }
    },

    showTip: function(item) {
        var me = this,
            tooltip,
            spriteTip,
            tipConfig,
            trackMouse,
            sprite,
            surface,
            surfaceExt,
            pos,
            x,
            y;
        if (!me.tooltip) {
            return;
        }
        clearTimeout(me.tipTimeout);
        tooltip = me.tooltip;
        spriteTip = me.spriteTip;
        tipConfig = me.tipConfig;
        trackMouse = tooltip.trackMouse;
        if (!trackMouse) {
            tooltip.trackMouse = true;
            sprite = item.sprite;
            surface = sprite.surface;
            surfaceExt = Ext.get(surface.getId());
            if (surfaceExt) {
                pos = surfaceExt.getXY();
                x = pos[0] + (sprite.attr.x || 0) + (sprite.attr.translation && sprite.attr.translation.x || 0);
                y = pos[1] + (sprite.attr.y || 0) + (sprite.attr.translation && sprite.attr.translation.y || 0);
                tooltip.targetXY = [x, y];
            }
        }
        if (spriteTip) {
            tipConfig.renderer.call(tooltip, item.storeItem, item, spriteTip.surface);
        } else {
            tipConfig.renderer.call(tooltip, item.storeItem, item);
        }
        tooltip.delayShow();
        tooltip.trackMouse = trackMouse;
    },

    hideTip: function(item) {
        var tooltip = this.tooltip;
        if (!tooltip) {
            return;
        }
        clearTimeout(this.tipTimeout);
        this.tipTimeout = setTimeout(function() {
            tooltip.delayHide();
        }, 0);
    }
});


Ext.define('Ext.chart.axis.Abstract', {

    

                                  

    
    
    

    

    
    constructor: function(config) {
        config = config || {};

        var me = this,
            pos = config.position || 'left';

        pos = pos.charAt(0).toUpperCase() + pos.substring(1);
        
        config.label = Ext.apply(config['axisLabel' + pos + 'Style'] || {}, config.label || {});
        config.axisTitleStyle = Ext.apply(config['axisTitle' + pos + 'Style'] || {}, config.labelTitle || {});
        Ext.apply(me, config);
        me.fields = Ext.Array.from(me.fields);
        this.callParent();
        me.labels = [];
        me.getId();
        me.labelGroup = me.chart.surface.getGroup(me.axisId + "-labels");
    },

    alignment: null,
    grid: false,
    steps: 10,
    x: 0,
    y: 0,
    minValue: 0,
    maxValue: 0,

    getId: function() {
        return this.axisId || (this.axisId = Ext.id(null, 'ext-axis-'));
    },

    
    processView: Ext.emptyFn,

    drawAxis: Ext.emptyFn,
    addDisplayAndLabels: Ext.emptyFn
});


Ext.define('Ext.chart.axis.Axis', {

    

    extend:  Ext.chart.axis.Abstract ,

    alternateClassName: 'Ext.chart.Axis',

                                

    

    

    

    

    

     
     hidden: false,

    
    forceMinMax: false,

    
    dashSize: 3,

    
    position: 'bottom',

    
    skipFirst: false,

    
    length: 0,

    
    width: 0,

    
    adjustEnd: true,

    majorTickSteps: false,
    
    nullGutters: { lower: 0, upper: 0, verticalAxis: undefined },

    
    applyData: Ext.emptyFn,

    getRange: function () {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            data = store.data.items,
            series = chart.series.items,
            position = me.position,
            axes,
            seriesClasses = Ext.chart.series,
            aggregations = [],
            min = Infinity, max = -Infinity,
            vertical = me.position === 'left' || me.position === 'right' || me.position === 'radial',
            i, ln, ln2, j, k, dataLength = data.length, aggregates,
            countedFields = {},
            allFields = {},
            excludable = true,
            fields, fieldMap, record, field, value;

        fields = me.fields;
        for (j = 0, ln = fields.length; j < ln; j++) {
            allFields[fields[j]] = true;
        }

        for (i = 0, ln = series.length; i < ln; i++) {
            if (series[i].seriesIsHidden) {
                continue;
            }
            if (!series[i].getAxesForXAndYFields) {
                continue;
            }
            axes = series[i].getAxesForXAndYFields();
            if (axes.xAxis && axes.xAxis !== position && axes.yAxis && axes.yAxis !== position) {
                
                continue;
            }

            if (seriesClasses.Bar && series[i] instanceof seriesClasses.Bar && !series[i].column) {
                
                fields = vertical ? Ext.Array.from(series[i].xField) : Ext.Array.from(series[i].yField);
            } else {
                fields = vertical ? Ext.Array.from(series[i].yField) : Ext.Array.from(series[i].xField);
            }

            if (me.fields.length) {
                for (j = 0, ln2 = fields.length; j < ln2; j++) {
                    if (allFields[fields[j]]) {
                        break;
                    }
                }
                if (j == ln2) {
                    
                    continue;
                }
            }

            if (aggregates = series[i].stacked) {
                
                if (seriesClasses.Bar && series[i] instanceof seriesClasses.Bar) {
                    if (series[i].column != vertical) {
                        aggregates = false;
                        excludable = false;
                    }
                }
                
                else if (!vertical) {
                    aggregates = false;
                    excludable = false;
                }
            }


            if (aggregates) {
                fieldMap = {};
                for (j = 0; j < fields.length; j++) {
                    if (excludable && series[i].__excludes && series[i].__excludes[j]) {
                        continue;
                    }
                    if (!allFields[fields[j]]) {
                        Ext.Logger.warn('Field `' + fields[j] + '` is not included in the ' + position + ' axis config.');
                    }
                    allFields[fields[j]] = fieldMap[fields[j]] = true;
                }
                aggregations.push({
                    fields: fieldMap,
                    positiveValue: 0,
                    negativeValue: 0
                });
            } else {

                if (!fields || fields.length == 0) {
                    fields = me.fields;
                }
                for (j = 0; j < fields.length; j++) {
                    if (excludable && series[i].__excludes && series[i].__excludes[j]) {
                        continue;
                    }
                    allFields[fields[j]] = countedFields[fields[j]] = true;
                }
            }
        }

        for (i = 0; i < dataLength; i++) {
            record = data[i];
            for (k = 0; k < aggregations.length; k++) {
                aggregations[k].positiveValue = 0;
                aggregations[k].negativeValue = 0;
            }
            for (field in allFields) {
                value = record.get(field);
                if (me.type == 'Time' && typeof value == "string") {
                    value = Date.parse(value);
                }
                if (isNaN(value)) {
                    continue;
                }
                if (value === undefined) {
                    value = 0;
                } else {
                    value = Number(value);
                }
                if (countedFields[field]) {
                    if (min > value) {
                        min = value;
                    }
                    if (max < value) {
                        max = value;
                    }
                }
                for (k = 0; k < aggregations.length; k++) {
                    if (aggregations[k].fields[field]) {

                        if (value >= 0) {
                            aggregations[k].positiveValue += value;
                            if (max < aggregations[k].positiveValue) {
                                max = aggregations[k].positiveValue;
                            }
                            
                            if (min > 0) {
                                min = 0;
                            }
                        } else {
                            aggregations[k].negativeValue += value;
                            if (min > aggregations[k].negativeValue) {
                                min = aggregations[k].negativeValue;
                            }
                            
                            if (max < 0) {
                                max = 0;
                            }
                        }
                    }
                }
            }
        }

        if (!isFinite(max)) {
            max = me.prevMax || 0;
        }
        if (!isFinite(min)) {
            min = me.prevMin || 0;
        }

        if (typeof min === 'number') {
            min = Ext.Number.correctFloat(min);
        }
         
        if (typeof max === 'number') {
            max = Ext.Number.correctFloat(max);
        }
        
        
        if (min != max && (max != Math.floor(max) || min != Math.floor(min))) {
            min = Math.floor(min);
            max = Math.floor(max) + 1;
        }

        if (!isNaN(me.minimum)) {
            min = me.minimum;
        }

        if (!isNaN(me.maximum)) {
            max = me.maximum;
        }

        if (min >= max) {
            
            min = Math.floor(min);
            max = min + 1;                
        }

        return {min: min, max: max};
    },

    
    calcEnds: function () {
        var me = this,
            range = me.getRange(),
            min = range.min,
            max = range.max,
            steps, prettyNumbers, out, changedRange;

        steps = (Ext.isNumber(me.majorTickSteps) ? me.majorTickSteps + 1 : me.steps);
        prettyNumbers = !(Ext.isNumber(me.maximum) && Ext.isNumber(me.minimum) && Ext.isNumber(me.majorTickSteps) && me.majorTickSteps > 0);

        out = Ext.draw.Draw.snapEnds(min, max, steps, prettyNumbers);

        if (Ext.isNumber(me.maximum)) {
            out.to = me.maximum;
            changedRange = true;
        }
        if (Ext.isNumber(me.minimum)) {
            out.from = me.minimum;
            changedRange = true;
        }
        if (me.adjustMaximumByMajorUnit) {
            out.to = Math.ceil(out.to / out.step) * out.step;
            changedRange = true;
        }
        if (me.adjustMinimumByMajorUnit) {
            out.from = Math.floor(out.from / out.step) * out.step;
            changedRange = true;
        }

        if (changedRange) {
            out.steps = Math.ceil((out.to - out.from) / out.step);            
        }

        me.prevMin = (min == max ? 0 : min);
        me.prevMax = max;
        return out;
    },


    
    drawAxis: function (init) {
        var me = this,
            i, 
            x = me.x,
            y = me.y,
            dashSize = me.dashSize,
            length = me.length,
            position = me.position,
            verticalAxis = (position == 'left' || position == 'right'),
            inflections = [],
            calcLabels = (me.isNumericAxis),
            stepCalcs = me.applyData(),
            step = stepCalcs.step,
            steps = stepCalcs.steps,
            stepsArray = Ext.isArray(steps),
            from = stepCalcs.from,
            to = stepCalcs.to,
            
            axisRange = (to - from) || 1,
            trueLength,
            currentX,
            currentY,
            path,
            subDashesX = me.minorTickSteps || 0,
            subDashesY = me.minorTickSteps || 0,
            dashesX = Math.max(subDashesX + 1, 0),
            dashesY = Math.max(subDashesY + 1, 0),
            dashDirection = (position == 'left' || position == 'top' ? -1 : 1),
            dashLength = dashSize * dashDirection,
            series = me.chart.series.items,
            firstSeries = series[0],
            gutters = Ext.clone(firstSeries ? firstSeries.nullGutters : me.nullGutters),
            seriesGutters,
            hasGutters,
            sameDirectionGutters,
            padding,
            subDashes,
            subDashValue,
            delta = 0,
            stepCount = 0,
            tick, axes, ln, val, begin, end;

        me.from = from;
        me.to = to;
        
        
        if (me.hidden || (from > to)) {
            return;
        }

        
        if ((stepsArray && (steps.length == 0)) || (!stepsArray && isNaN(step))) {
            return;
        }

        if (stepsArray) {
            
            
            steps = Ext.Array.filter(steps, function(elem, index, array) {
                return (+elem > +me.from && +elem < +me.to);
            }, this);

            
            steps = Ext.Array.union([me.from], steps, [me.to]);
        }
        else {
            
            steps = new Array;
            for (val = +me.from; val < +me.to; val += step) {
                steps.push(val);
            }
            steps.push(+me.to);
        }
        stepCount = steps.length;


        
        for (i = 0, ln = series.length; i < ln; i++) {
            if (series[i].seriesIsHidden) {
                continue;
            }
            if (!series[i].getAxesForXAndYFields) {
                continue;
            }
            axes = series[i].getAxesForXAndYFields();
            if (!axes.xAxis || !axes.yAxis || (axes.xAxis === position) || (axes.yAxis === position)) {
                seriesGutters = Ext.clone(series[i].getGutters());
                hasGutters = (seriesGutters.verticalAxis !== undefined);
                sameDirectionGutters = (hasGutters && (seriesGutters.verticalAxis == verticalAxis));
                if (hasGutters) {
                    if (!sameDirectionGutters) {
                        
                        
                        
                        
                        padding = series[i].getPadding();
                        if (verticalAxis) {
                            seriesGutters = { lower: padding.bottom, upper: padding.top, verticalAxis: true };
                        } else {
                            seriesGutters = { lower: padding.left, upper: padding.right, verticalAxis: false };
                        }
                    }
                    if (gutters.lower < seriesGutters.lower) {
                        gutters.lower = seriesGutters.lower;
                    }
                    if (gutters.upper < seriesGutters.upper) {
                        gutters.upper = seriesGutters.upper;
                    }
                    gutters.verticalAxis = verticalAxis;
                }
            }
        }

        

        if (calcLabels) {
            me.labels = [];
        }

        if (gutters) {
            if (verticalAxis) {
                currentX = Math.floor(x);
                path = ["M", currentX + 0.5, y, "l", 0, -length];
                trueLength = length - (gutters.lower + gutters.upper);

                for (tick = 0; tick < stepCount; tick++) {
                    currentY = y - gutters.lower - (steps[tick] - steps[0]) * trueLength / axisRange;
                    path.push("M", currentX, Math.floor(currentY) + 0.5, "l", dashLength * 2, 0);

                    inflections.push([ currentX, Math.floor(currentY) ]);

                    if (calcLabels) {
                        me.labels.push(steps[tick]);
                    }
                }
            } else {
                currentY = Math.floor(y);
                path = ["M", x, currentY + 0.5, "l", length, 0];
                trueLength = length - (gutters.lower + gutters.upper);

                for (tick = 0; tick < stepCount; tick++) {
                    currentX = x + gutters.lower + (steps[tick] - steps[0]) * trueLength / axisRange;
                    path.push("M", Math.floor(currentX) + 0.5, currentY, "l", 0, dashLength * 2 + 1);

                    inflections.push([ Math.floor(currentX), currentY ]);

                    if (calcLabels) {
                        me.labels.push(steps[tick]);
                    }
                }
            }
        }


        

        
        
        
        
        subDashes = (verticalAxis ? subDashesY : subDashesX);
        if (Ext.isArray(subDashes)) {
            if (subDashes.length == 2) {
                subDashValue = +Ext.Date.add(new Date(), subDashes[0], subDashes[1]) - Date.now();
            } else {
                subDashValue = subDashes[0];
            }
        }
        else {
            if (Ext.isNumber(subDashes) && subDashes > 0) {
                subDashValue = step / (subDashes + 1);
            }
        }

        if (gutters && subDashValue) {
            for (tick = 0; tick < stepCount - 1; tick++) {
                begin = +steps[tick];
                end = +steps[tick+1];
                if (verticalAxis) {
                    for (value = begin + subDashValue; value < end; value += subDashValue) {
                        currentY = y - gutters.lower - (value - steps[0]) * trueLength / axisRange;
                        path.push("M", currentX, Math.floor(currentY) + 0.5, "l", dashLength, 0);
                    }
                }
                else {
                    for (value = begin + subDashValue; value < end; value += subDashValue) {
                        currentX = x + gutters.upper + (value - steps[0]) * trueLength / axisRange;
                        path.push("M", Math.floor(currentX) + 0.5, currentY, "l", 0, dashLength + 1);
                    }
                }
            }            
        }


        

        if (!me.axis) {
            me.axis = me.chart.surface.add(Ext.apply({
                type: 'path',
                path: path
            }, me.axisStyle));
        }
        me.axis.setAttributes({
            path: path
        }, true);
        me.inflections = inflections;
        if (!init && me.grid) {
            me.drawGrid();
        }
        me.axisBBox = me.axis.getBBox();
        me.drawLabel();
    },

    
    drawGrid: function () {
        var me = this,
            surface = me.chart.surface,
            grid = me.grid,
            odd = grid.odd,
            even = grid.even,
            inflections = me.inflections,
            ln = inflections.length - ((odd || even) ? 0 : 1),
            position = me.position,
            maxGutters = me.chart.maxGutters,
            width = me.width - 2,
            point, prevPoint,
            i = 1,
            path = [], styles, lineWidth, dlineWidth,
            oddPath = [], evenPath = [];

        if (((maxGutters.bottom !== 0 || maxGutters.top !== 0) && (position == 'left' || position == 'right')) ||
            ((maxGutters.left !== 0 || maxGutters.right !== 0) && (position == 'top' || position == 'bottom'))) {
            i = 0;
            ln++;
        }
        for (; i < ln; i++) {
            point = inflections[i];
            prevPoint = inflections[i - 1];
            if (odd || even) {
                path = (i % 2) ? oddPath : evenPath;
                styles = ((i % 2) ? odd : even) || {};
                lineWidth = (styles.lineWidth || styles['stroke-width'] || 0) / 2;
                dlineWidth = 2 * lineWidth;
                if (position == 'left') {
                    path.push("M", prevPoint[0] + 1 + lineWidth, prevPoint[1] + 0.5 - lineWidth,
                        "L", prevPoint[0] + 1 + width - lineWidth, prevPoint[1] + 0.5 - lineWidth,
                        "L", point[0] + 1 + width - lineWidth, point[1] + 0.5 + lineWidth,
                        "L", point[0] + 1 + lineWidth, point[1] + 0.5 + lineWidth, "Z");
                }
                else if (position == 'right') {
                    path.push("M", prevPoint[0] - lineWidth, prevPoint[1] + 0.5 - lineWidth,
                        "L", prevPoint[0] - width + lineWidth, prevPoint[1] + 0.5 - lineWidth,
                        "L", point[0] - width + lineWidth, point[1] + 0.5 + lineWidth,
                        "L", point[0] - lineWidth, point[1] + 0.5 + lineWidth, "Z");
                }
                else if (position == 'top') {
                    path.push("M", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] + 1 + lineWidth,
                        "L", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] + 1 + width - lineWidth,
                        "L", point[0] + 0.5 - lineWidth, point[1] + 1 + width - lineWidth,
                        "L", point[0] + 0.5 - lineWidth, point[1] + 1 + lineWidth, "Z");
                }
                else {
                    path.push("M", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] - lineWidth,
                        "L", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] - width + lineWidth,
                        "L", point[0] + 0.5 - lineWidth, point[1] - width + lineWidth,
                        "L", point[0] + 0.5 - lineWidth, point[1] - lineWidth, "Z");
                }
            } else {
                if (position == 'left') {
                    path = path.concat(["M", point[0] + 0.5, point[1] + 0.5, "l", width, 0]);
                }
                else if (position == 'right') {
                    path = path.concat(["M", point[0] - 0.5, point[1] + 0.5, "l", -width, 0]);
                }
                else if (position == 'top') {
                    path = path.concat(["M", point[0] + 0.5, point[1] + 0.5, "l", 0, width]);
                }
                else {
                    path = path.concat(["M", point[0] + 0.5, point[1] - 0.5, "l", 0, -width]);
                }
            }
        }
        if (odd || even) {
            if (oddPath.length) {
                if (!me.gridOdd && oddPath.length) {
                    me.gridOdd = surface.add({
                        type: 'path',
                        path: oddPath
                    });
                }
                me.gridOdd.setAttributes(Ext.apply({
                    path: oddPath,
                    hidden: false
                }, odd || {}), true);
            }
            if (evenPath.length) {
                if (!me.gridEven) {
                    me.gridEven = surface.add({
                        type: 'path',
                        path: evenPath
                    });
                }
                me.gridEven.setAttributes(Ext.apply({
                    path: evenPath,
                    hidden: false
                }, even || {}), true);
            }
        }
        else {
            if (path.length) {
                if (!me.gridLines) {
                    me.gridLines = me.chart.surface.add({
                        type: 'path',
                        path: path,
                        "stroke-width": me.lineWidth || 1,
                        stroke: me.gridColor || '#ccc'
                    });
                }
                me.gridLines.setAttributes({
                    hidden: false,
                    path: path
                }, true);
            }
            else if (me.gridLines) {
                me.gridLines.hide(true);
            }
        }
    },

    
    getOrCreateLabel: function (i, text) {
        var me = this,
            labelGroup = me.labelGroup,
            textLabel = labelGroup.getAt(i),
            surface = me.chart.surface;
        if (textLabel) {
            if (text != textLabel.attr.text) {
                textLabel.setAttributes(Ext.apply({
                    text: text
                }, me.label), true);
                textLabel._bbox = textLabel.getBBox();
            }
        }
        else {
            textLabel = surface.add(Ext.apply({
                group: labelGroup,
                type: 'text',
                x: 0,
                y: 0,
                text: text
            }, me.label));
            surface.renderItem(textLabel);
            textLabel._bbox = textLabel.getBBox();
        }
        
        if (me.label.rotation) {
            textLabel.setAttributes({
                rotation: {
                    degrees: 0
                }
            }, true);
            textLabel._ubbox = textLabel.getBBox();
            textLabel.setAttributes(me.label, true);
        } else {
            textLabel._ubbox = textLabel._bbox;
        }
        return textLabel;
    },

    rect2pointArray: function (sprite) {
        var surface = this.chart.surface,
            rect = surface.getBBox(sprite, true),
            p1 = [rect.x, rect.y],
            p1p = p1.slice(),
            p2 = [rect.x + rect.width, rect.y],
            p2p = p2.slice(),
            p3 = [rect.x + rect.width, rect.y + rect.height],
            p3p = p3.slice(),
            p4 = [rect.x, rect.y + rect.height],
            p4p = p4.slice(),
            matrix = sprite.matrix;
        
        p1[0] = matrix.x.apply(matrix, p1p);
        p1[1] = matrix.y.apply(matrix, p1p);

        p2[0] = matrix.x.apply(matrix, p2p);
        p2[1] = matrix.y.apply(matrix, p2p);

        p3[0] = matrix.x.apply(matrix, p3p);
        p3[1] = matrix.y.apply(matrix, p3p);

        p4[0] = matrix.x.apply(matrix, p4p);
        p4[1] = matrix.y.apply(matrix, p4p);
        return [p1, p2, p3, p4];
    },

    intersect: function (l1, l2) {
        var r1 = this.rect2pointArray(l1),
            r2 = this.rect2pointArray(l2);
        return !!Ext.draw.Draw.intersect(r1, r2).length;
    },

    drawHorizontalLabels: function () {
        var me = this,
            labelConf = me.label,
            floor = Math.floor,
            max = Math.max,
            axes = me.chart.axes,
            insetPadding = me.chart.insetPadding,
            gutters = me.chart.maxGutters,
            position = me.position,
            inflections = me.inflections,
            ln = inflections.length,
            labels = me.labels,
            maxHeight = 0,
            ratio,
            bbox, point, prevLabel, prevLabelId,
            adjustEnd = me.adjustEnd,
            hasLeft = axes.findIndex('position', 'left') != -1,
            hasRight = axes.findIndex('position', 'right') != -1,
            reverse = me.reverse,
            textLabel, text, idx,
            last, x, y, i, firstLabel;

        last = ln - 1;
        
        point = inflections[0];
        firstLabel = me.getOrCreateLabel(0, me.label.renderer(labels[0]));
        ratio = Math.floor(Math.abs(Math.sin(labelConf.rotate && (labelConf.rotate.degrees * Math.PI / 180) || 0)));

        for (i = 0; i < ln; i++) {
            point = inflections[i];
            idx = i;
            if (reverse) {
                idx = ln - i - 1;
            }
            text = me.label.renderer(labels[idx]);
            textLabel = me.getOrCreateLabel(i, text);
            bbox = textLabel._bbox;
            maxHeight = max(maxHeight, bbox.height + me.dashSize + me.label.padding);
            x = floor(point[0] - (ratio ? bbox.height : bbox.width) / 2);
            if (adjustEnd && gutters.left == 0 && gutters.right == 0) {
                if (i == 0 && !hasLeft) {
                    x = point[0];
                }
                else if (i == last && !hasRight) {
                    x = Math.min(x, point[0] - bbox.width + insetPadding);
                }
            }
            if (position == 'top') {
                y = point[1] - (me.dashSize * 2) - me.label.padding - (bbox.height / 2);
            }
            else {
                y = point[1] + (me.dashSize * 2) + me.label.padding + (bbox.height / 2);
            }

            textLabel.setAttributes({
                hidden: false,
                x: x,
                y: y
            }, true);

            
            if (i != 0 && (me.intersect(textLabel, prevLabel)
                || me.intersect(textLabel, firstLabel))) {
                if (i === last && prevLabelId !== 0) {
                    prevLabel.hide(true);
                } else {
                    textLabel.hide(true);
                    continue;
                }
            }

            prevLabel = textLabel;
            prevLabelId = i;
        }

        return maxHeight;
    },

    drawVerticalLabels: function () {
        var me = this,
            inflections = me.inflections,
            position = me.position,
            ln = inflections.length,
            chart = me.chart,
            insetPadding = chart.insetPadding,
            labels = me.labels,
            maxWidth = 0,
            max = Math.max,
            floor = Math.floor,
            ceil = Math.ceil,
            axes = me.chart.axes,
            gutters = me.chart.maxGutters,
            bbox, point, prevLabel, prevLabelId,
            hasTop = axes.findIndex('position', 'top') != -1,
            hasBottom = axes.findIndex('position', 'bottom') != -1,
            adjustEnd = me.adjustEnd,
            textLabel, text,
            last = ln - 1, x, y, i;

        for (i = 0; i < ln; i++) {
            point = inflections[i];
            text = me.label.renderer(labels[i]);
            textLabel = me.getOrCreateLabel(i, text);
            bbox = textLabel._bbox;

            maxWidth = max(maxWidth, bbox.width + me.dashSize + me.label.padding);
            y = point[1];
            if (adjustEnd && (gutters.bottom + gutters.top) < bbox.height / 2) {
                if (i == last && !hasTop) {
                    y = Math.max(y, me.y - me.length + ceil(bbox.height / 2) - insetPadding);
                }
                else if (i == 0 && !hasBottom) {
                    y = me.y + gutters.bottom - floor(bbox.height / 2);
                }
            }
            if (position == 'left') {
                x = point[0] - bbox.width - me.dashSize - me.label.padding - 2;
            }
            else {
                x = point[0] + me.dashSize + me.label.padding + 2;
            }
            textLabel.setAttributes(Ext.apply({
                hidden: false,
                x: x,
                y: y
            }, me.label), true);
            
            if (i != 0 && me.intersect(textLabel, prevLabel)) {
                if (i === last && prevLabelId !== 0) {
                    prevLabel.hide(true);
                } else {
                    textLabel.hide(true);
                    continue;
                }
            }
            prevLabel = textLabel;
            prevLabelId = i;
        }

        return maxWidth;
    },

    
    drawLabel: function () {
        var me = this,
            position = me.position,
            labelGroup = me.labelGroup,
            inflections = me.inflections,
            maxWidth = 0,
            maxHeight = 0,
            ln, i;

        if (position == 'left' || position == 'right') {
            maxWidth = me.drawVerticalLabels();
        } else {
            maxHeight = me.drawHorizontalLabels();
        }

        
        ln = labelGroup.getCount();
        i = inflections.length;
        for (; i < ln; i++) {
            labelGroup.getAt(i).hide(true);
        }

        me.bbox = {};
        Ext.apply(me.bbox, me.axisBBox);
        me.bbox.height = maxHeight;
        me.bbox.width = maxWidth;
        if (Ext.isString(me.title)) {
            me.drawTitle(maxWidth, maxHeight);
        }
    },

    
    setTitle: function (title) {
        this.title = title;
        this.drawLabel();
    },

    
    drawTitle: function (maxWidth, maxHeight) {
        var me = this,
            position = me.position,
            surface = me.chart.surface,
            displaySprite = me.displaySprite,
            title = me.title,
            rotate = (position == 'left' || position == 'right'),
            x = me.x,
            y = me.y,
            base, bbox, pad;

        if (displaySprite) {
            displaySprite.setAttributes({text: title}, true);
        } else {
            base = {
                type: 'text',
                x: 0,
                y: 0,
                text: title
            };
            displaySprite = me.displaySprite = surface.add(Ext.apply(base, me.axisTitleStyle, me.labelTitle));
            surface.renderItem(displaySprite);
        }
        bbox = displaySprite.getBBox();
        pad = me.dashSize + me.label.padding;

        if (rotate) {
            y -= ((me.length / 2) - (bbox.height / 2));
            if (position == 'left') {
                x -= (maxWidth + pad + (bbox.width / 2));
            }
            else {
                x += (maxWidth + pad + bbox.width - (bbox.width / 2));
            }
            me.bbox.width += bbox.width + 10;
        }
        else {
            x += (me.length / 2) - (bbox.width * 0.5);
            if (position == 'top') {
                y -= (maxHeight + pad + (bbox.height * 0.3));
            }
            else {
                y += (maxHeight + pad + (bbox.height * 0.8));
            }
            me.bbox.height += bbox.height + 10;
        }
        displaySprite.setAttributes({
            translate: {
                x: x,
                y: y
            }
        }, true);
    }
});


Ext.define('Ext.chart.axis.Category', {

    

    extend:  Ext.chart.axis.Axis ,

    alternateClassName: 'Ext.chart.CategoryAxis',

    alias: 'axis.category',

    
    isCategoryAxis: true,

    

    
    doConstrain: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            items = store.data.items,
            series = chart.series.items,
            seriesLength = series.length,
            data = [], i

        for (i = 0; i < seriesLength; i++) {
            if (series[i].type === 'bar' && series[i].stacked) {
                
                return;
            }
        }

        for (i = me.minimum; i < me.maximum; i++) {
            data.push(items[i]);
        }
        
        chart.setSubStore(new Ext.data.Store({
            model: store.model,
            data: data
        }));
    },

    
    setLabels: function() {
        var store = this.chart.getChartStore(),
            data = store.data.items,
            d, dLen, record,
            fields = this.fields,
            ln = fields.length,
            labels,
            name,
            i;

        labels = this.labels = [];
        for (d = 0, dLen = data.length; d < dLen; d++) {
            record = data[d];
            for (i = 0; i < ln; i++) {
                name = record.get(fields[i]);
                labels.push(name);
            }
        }
    },

    
    applyData: function() {
        this.callParent();
        this.setLabels();
        var count = this.chart.getChartStore().getCount();
        return {
            from: 0,
            to: count - 1,
            power: 1,
            step: 1,
            steps: count - 1
        };
    }
});


Ext.define('Ext.chart.axis.Gauge', {

    

    extend:  Ext.chart.axis.Abstract ,

    

    

    

    

    

    

    position: 'gauge',

    alias: 'axis.gauge',

    drawAxis: function(init) {
        var chart = this.chart,
            surface = chart.surface,
            bbox = chart.chartBBox,
            centerX = bbox.x + (bbox.width / 2),
            centerY = bbox.y + bbox.height,
            margin = this.margin || 10,
            rho = Math.min(bbox.width, 2 * bbox.height) /2 + margin,
            sprites = [], sprite,
            steps = this.steps,
            i, pi = Math.PI,
            cos = Math.cos,
            sin = Math.sin;

        if (this.sprites && !chart.resizing) {
            this.drawLabel();
            return;
        }

        if (this.margin >= 0) {
            if (!this.sprites) {
                
                for (i = 0; i <= steps; i++) {
                    sprite = surface.add({
                        type: 'path',
                        path: ['M', centerX + (rho - margin) * cos(i / steps * pi - pi),
                                    centerY + (rho - margin) * sin(i / steps * pi - pi),
                                    'L', centerX + rho * cos(i / steps * pi - pi),
                                    centerY + rho * sin(i / steps * pi - pi), 'Z'],
                        stroke: '#ccc'
                    });
                    sprite.setAttributes({
                        hidden: false
                    }, true);
                    sprites.push(sprite);
                }
            } else {
                sprites = this.sprites;
                
                for (i = 0; i <= steps; i++) {
                    sprites[i].setAttributes({
                        path: ['M', centerX + (rho - margin) * cos(i / steps * pi - pi),
                                    centerY + (rho - margin) * sin(i / steps * pi - pi),
                               'L', centerX + rho * cos(i / steps * pi - pi),
                                    centerY + rho * sin(i / steps * pi - pi), 'Z'],
                        stroke: '#ccc'
                    }, true);
                }
            }
        }
        this.sprites = sprites;
        this.drawLabel();
        if (this.title) {
            this.drawTitle();
        }
    },

    drawTitle: function() {
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            bbox = chart.chartBBox,
            labelSprite = me.titleSprite,
            labelBBox;

        if (!labelSprite) {
            me.titleSprite = labelSprite = surface.add(Ext.apply({
                type: 'text',
                zIndex: 2
            }, me.axisTitleStyle, me.labelTitle));
        }
        labelSprite.setAttributes(Ext.apply({
            text: me.title
        }, me.label || {}), true);
        labelBBox = labelSprite.getBBox();
        labelSprite.setAttributes({
            x: bbox.x + (bbox.width / 2) - (labelBBox.width / 2),
            y: bbox.y + bbox.height - (labelBBox.height / 2) - 4
        }, true);
    },

    
    setTitle: function(title) {
        this.title = title;
        this.drawTitle();
    },

    drawLabel: function() {
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            bbox = chart.chartBBox,
            centerX = bbox.x + (bbox.width / 2),
            centerY = bbox.y + bbox.height,
            margin = me.margin || 10,
            rho = Math.min(bbox.width, 2 * bbox.height) /2 + 2 * margin,
            round = Math.round,
            labelArray = [], label,
            maxValue = me.maximum || 0,
            minValue = me.minimum || 0,
            steps = me.steps, 
            pi = Math.PI,
            cos = Math.cos,
            sin = Math.sin,
            labelConf = this.label,
            renderer = labelConf.renderer || Ext.identityFn,
            reverse = me.reverse,
            i, adjY, idx;

        if (!this.labelArray) {
            
            for (i = 0; i <= steps; i++) {
                
                adjY = (i === 0 || i === steps) ? 7 : 0;
                idx = reverse ? steps - i : i;
                label = surface.add({
                    type: 'text',
                    text: renderer(round(minValue + idx / steps * (maxValue - minValue))),
                    x: centerX + rho * cos(i / steps * pi - pi),
                    y: centerY + rho * sin(i / steps * pi - pi) - adjY,
                    'text-anchor': 'middle',
                    'stroke-width': 0.2,
                    zIndex: 10,
                    stroke: '#333'
                });
                label.setAttributes({
                    hidden: false
                }, true);
                labelArray.push(label);
            }
        }
        else {
            labelArray = this.labelArray;
            
            for (i = 0; i <= steps; i++) {
                
                adjY = (i === 0 || i === steps) ? 7 : 0;
                idx = reverse ? steps - i : i;
                labelArray[i].setAttributes({
                    text: renderer(round(minValue + idx / steps * (maxValue - minValue))),
                    x: centerX + rho * cos(i / steps * pi - pi),
                    y: centerY + rho * sin(i / steps * pi - pi) - adjY
                }, true);
            }
        }
        this.labelArray = labelArray;
    }
});


Ext.define('Ext.chart.axis.Numeric', {

    

    extend:  Ext.chart.axis.Axis ,

    alternateClassName: 'Ext.chart.NumericAxis',

    

    type: 'Numeric',

    
    isNumericAxis: true,

    alias: 'axis.numeric',

                             

    constructor: function(config) {
        var me = this,
            hasLabel = !!(config.label && config.label.renderer),
            label;

        me.callParent([config]);
        label = me.label;

        if (config.constrain == null) {
            me.constrain = (config.minimum != null && config.maximum != null);
        }

        if (!hasLabel) {
            label.renderer = function(v) {
                return me.roundToDecimal(v, me.decimals);
            };
        }
    },

    roundToDecimal: function(v, dec) {
        var val = Math.pow(10, dec || 0);
        return Math.round(v * val) / val;
    },

    
    minimum: NaN,

    
    maximum: NaN,

    
    constrain: true,

    
    decimals: 2,

    
    scale: "linear",

    
    doConstrain: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            items = store.data.items,
            d, dLen, record,
            series = chart.series.items,
            fields = me.fields,
            ln = fields.length,
            range = me.calcEnds(),
            min = range.from, max = range.to, i, l,
            useAcum = false,
            value, data = [],
            addRecord;

        for (d = 0, dLen = items.length; d < dLen; d++) {
            addRecord = true;
            record = items[d];
            for (i = 0; i < ln; i++) {
                value = record.get(fields[i]);
                if (me.type == 'Time' && typeof value == "string") {
                    value = Date.parse(value);
                }
                if (+value < +min) {
                    addRecord = false;
                    break;
                }
                if (+value > +max) {
                    addRecord = false;
                    break;
                }
            }
            if (addRecord) {
                data.push(record);
            }
        }
        
        chart.setSubStore(new Ext.data.Store({
            model: store.model,
            data: data
        }));
    },
    
    position: 'left',

    
    adjustMaximumByMajorUnit: false,

    
    adjustMinimumByMajorUnit: false,

    
    processView: function() {
        var me = this,
            chart = me.chart,
            series = chart.series.items,
            i, l;

        for (i = 0, l = series.length; i < l; i++) {
            if (series[i].stacked) {
                
                delete me.minimum;
                delete me.maximum;
                me.constrain = false;
                break;
            }
        }

        if (me.constrain) {
            me.doConstrain();
        }
    },

    
    applyData: function() {
        this.callParent();
        return this.calcEnds();
    }
});


Ext.define('Ext.chart.axis.Radial', {

    

    extend:  Ext.chart.axis.Numeric ,

    

    position: 'radial',

    alias: 'axis.radial',

    

    

    drawAxis: function(init) {
        var chart = this.chart,
            surface = chart.surface,
            bbox = chart.chartBBox,
            store = chart.getChartStore(),
            l = store.getCount(),
            centerX = bbox.x + (bbox.width / 2),
            centerY = bbox.y + (bbox.height / 2),
            rho = Math.min(bbox.width, bbox.height) /2,
            sprites = [], sprite,
            steps = this.steps,
            i, j, pi2 = Math.PI * 2,
            cos = Math.cos, sin = Math.sin;

        if (this.sprites && !chart.resizing) {
            this.drawLabel();
            return;
        }

        if (!this.sprites) {
            
            for (i = 1; i <= steps; i++) {
                sprite = surface.add({
                    type: 'circle',
                    x: centerX,
                    y: centerY,
                    radius: Math.max(rho * i / steps, 0),
                    stroke: '#ccc'
                });
                sprite.setAttributes({
                    hidden: false
                }, true);
                sprites.push(sprite);
            }
            
            for (i = 0; i < l; i++) {
                sprite = surface.add({
                    type: 'path',
                    path: ['M', centerX, centerY, 'L', centerX + rho * cos(i / l * pi2), centerY + rho * sin(i / l * pi2), 'Z'],
                    stroke: '#ccc'
                });
                sprite.setAttributes({
                    hidden: false
                }, true);
                sprites.push(sprite);
            }
        } else {
            sprites = this.sprites;
            
            for (i = 0; i < steps; i++) {
                sprites[i].setAttributes({
                    x: centerX,
                    y: centerY,
                    radius: Math.max(rho * (i + 1) / steps, 0),
                    stroke: '#ccc'
                }, true);
            }
            
            for (j = 0; j < l; j++) {
                sprites[i + j].setAttributes({
                    path: ['M', centerX, centerY, 'L', centerX + rho * cos(j / l * pi2), centerY + rho * sin(j / l * pi2), 'Z'],
                    stroke: '#ccc'
                }, true);
            }
        }
        this.sprites = sprites;

        this.drawLabel();
    },

    drawLabel: function() {
        var chart = this.chart,
            seriesItems = chart.series.items,
            series,
            surface = chart.surface,
            bbox = chart.chartBBox,
            store = chart.getChartStore(),
            data = store.data.items,
            ln, record,
            centerX = bbox.x + (bbox.width / 2),
            centerY = bbox.y + (bbox.height / 2),
            rho = Math.min(bbox.width, bbox.height) /2,
            max = Math.max, round = Math.round,
            labelArray = [], label,
            fields = [], nfields,
            categories = [], xField,
            aggregate = !this.maximum,
            maxValue = this.maximum || 0,
            steps = this.steps, i = 0, j, dx, dy,
            pi2 = Math.PI * 2,
            cos = Math.cos, sin = Math.sin,
            display = this.label.display,
            draw = display !== 'none',
            margin = 10;

        if (!draw) {
            return;
        }

        
        for (i = 0, ln = seriesItems.length; i < ln; i++) {
            series = seriesItems[i];
            fields.push(series.yField);
            xField = series.xField;
        }
        
        
        for (j = 0, ln = data.length; j < ln; j++) {
            record = data[j];
            categories.push(record.get(xField));

            if (aggregate) {
                for (i = 0, nfields = fields.length; i < nfields; i++) {
                    maxValue = max(+record.get(fields[i]), maxValue);
                }
            }
        }
        if (!this.labelArray) {
            if (display != 'categories') {
                
                for (i = 1; i <= steps; i++) {
                    label = surface.add({
                        type: 'text',
                        text: round(i / steps * maxValue),
                        x: centerX,
                        y: centerY - rho * i / steps,
                        'text-anchor': 'middle',
                        'stroke-width': 0.1,
                        stroke: '#333'
                    });
                    label.setAttributes({
                        hidden: false
                    }, true);
                    labelArray.push(label);
                }
            }
            if (display != 'scale') {
                
                for (j = 0, steps = categories.length; j < steps; j++) {
                    dx = cos(j / steps * pi2) * (rho + margin);
                    dy = sin(j / steps * pi2) * (rho + margin);
                    label = surface.add({
                        type: 'text',
                        text: categories[j],
                        x: centerX + dx,
                        y: centerY + dy,
                        'text-anchor': dx * dx <= 0.001? 'middle' : (dx < 0? 'end' : 'start')
                    });
                    label.setAttributes({
                        hidden: false
                    }, true);
                    labelArray.push(label);
                }
            }
        }
        else {
            labelArray = this.labelArray;
            if (display != 'categories') {
                
                for (i = 0; i < steps; i++) {
                    labelArray[i].setAttributes({
                        text: round((i + 1) / steps * maxValue),
                        x: centerX,
                        y: centerY - rho * (i + 1) / steps,
                        'text-anchor': 'middle',
                        'stroke-width': 0.1,
                        stroke: '#333'
                    }, true);
                }
            }
            if (display != 'scale') {
                
                for (j = 0, steps = categories.length; j < steps; j++) {
                    dx = cos(j / steps * pi2) * (rho + margin);
                    dy = sin(j / steps * pi2) * (rho + margin);
                    if (labelArray[i + j]) {
                        labelArray[i + j].setAttributes({
                            type: 'text',
                            text: categories[j],
                            x: centerX + dx,
                            y: centerY + dy,
                            'text-anchor': dx * dx <= 0.001? 'middle' : (dx < 0? 'end' : 'start')
                        }, true);
                    }
                }
            }
        }
        this.labelArray = labelArray;
    },

    getRange: function () {
        var range = this.callParent();
        range.min = 0;  
        return range;
    },

    processView: function() {
        var me = this,
            seriesItems = me.chart.series.items,
            i, ln, series, ends, fields = [];

        for (i = 0, ln = seriesItems.length; i < ln; i++) {
            series = seriesItems[i];
            fields.push(series.yField);
        }
        me.fields = fields;

        ends = me.calcEnds();
        me.maximum = ends.to;
        me.steps = ends.steps;
    }
});


Ext.define('Ext.chart.axis.Time', {

    

    extend:  Ext.chart.axis.Numeric ,

    alternateClassName: 'Ext.chart.TimeAxis',

    type: 'Time',

    alias: 'axis.time',

                             

    

    
    dateFormat: false,

    
    fromDate: false,

    
    toDate: false,

    
    step: [Ext.Date.DAY, 1],

    
    constrain: false,

    constructor: function (config) {
        var me = this, label, f, df;
        me.callParent([config]);
        label = me.label || {};
        df = this.dateFormat;
        if (df) {
            if (label.renderer) {
                f = label.renderer;
                label.renderer = function(v) {
                    v = f(v);
                    return Ext.Date.format(new Date(f(v)), df);
                };
            } else {
                label.renderer = function(v) {
                    return Ext.Date.format(new Date(v >> 0), df);
                };
            }
        }
    },

    
    processView: function () {
        var me = this;
        if (me.fromDate) {
            me.minimum = +me.fromDate;
        }
        if (me.toDate) {
            me.maximum = +me.toDate;
        }
        if(me.constrain){
            me.doConstrain();
        }
     },

    
    calcEnds: function() {
        var me = this, range, step = me.step;
        if (step) {
            range = me.getRange();
            range = Ext.draw.Draw.snapEndsByDateAndStep(new Date(range.min), new Date(range.max), Ext.isNumber(step) ? [Date.MILLI, step]: step);
            if (me.minimum) {
                range.from = me.minimum;
            }
            if (me.maximum) {
                range.to = me.maximum;
            }
            return range;
        } else {
            return me.callParent(arguments);
        }
    }
 });



Ext.define('Ext.chart.series.Series', {

    

    mixins: {
        observable:  Ext.util.Observable ,
        labels:  Ext.chart.Label ,
        highlights:  Ext.chart.Highlight ,
        tips:  Ext.chart.Tip ,
        callouts:  Ext.chart.Callout 
    },

    

    

    

    
    type: null,

    
    title: null,

    
    showInLegend: true,

    
    renderer: function(sprite, record, attributes, index, store) {
        return attributes;
    },

    
    shadowAttributes: null,

    
    animating: false,

    
    nullGutters: { lower: 0, upper: 0, verticalAxis: undefined },

    
    nullPadding: { left:0, right:0, width:0, bottom:0, top:0, height:0 },

    constructor: function(config) {
        var me = this;
        if (config) {
            Ext.apply(me, config);
        }

        me.shadowGroups = [];

        me.mixins.labels.constructor.call(me, config);
        me.mixins.highlights.constructor.call(me, config);
        me.mixins.tips.constructor.call(me, config);
        me.mixins.callouts.constructor.call(me, config);

        me.addEvents({
            scope: me,
            
            
            itemclick: true,
            
            
            itemdblclick: true,
            
            
            itemmouseover: true,
            
            
            itemmouseout: true,
            
            
            itemmousedown: true,
            
            
            itemmouseup: true,
            
            mouseleave: true,
            
            
            afterdraw: true,

            
            titlechange: true
        });

        me.mixins.observable.constructor.call(me, config);

        me.on({
            scope: me,
            itemmouseover: me.onItemMouseOver,
            itemmouseout: me.onItemMouseOut,
            mouseleave: me.onMouseLeave
        });
        
        if (me.style) {
            Ext.apply(me.seriesStyle, me.style);
        }
    },
    
    initialize: Ext.emptyFn,
    
    onRedraw: Ext.emptyFn,
    
    
    eachRecord: function(fn, scope) {
        var chart = this.chart;
        chart.getChartStore().each(fn, scope);
    },

    
    getRecordCount: function() {
        var chart = this.chart,
            store = chart.getChartStore();
        return store ? store.getCount() : 0;
    },

    
    isExcluded: function(index) {
        var excludes = this.__excludes;
        return !!(excludes && excludes[index]);
    },

    
    setBBox: function(noGutter) {
        var me = this,
            chart = me.chart,
            chartBBox = chart.chartBBox,
            maxGutters = noGutter ? { left: 0, right: 0, bottom: 0, top: 0 } : chart.maxGutters,
            clipBox, bbox;

        clipBox = {
            x: chartBBox.x,
            y: chartBBox.y,
            width: chartBBox.width,
            height: chartBBox.height
        };
        me.clipBox = clipBox;

        bbox = {
            x: (clipBox.x + maxGutters.left) - (chart.zoom.x * chart.zoom.width),
            y: (clipBox.y + maxGutters.bottom) - (chart.zoom.y * chart.zoom.height),
            width: (clipBox.width - (maxGutters.left + maxGutters.right)) * chart.zoom.width,
            height: (clipBox.height - (maxGutters.bottom + maxGutters.top)) * chart.zoom.height
        };
        me.bbox = bbox;
    },

    
    onAnimate: function(sprite, attr) {
        var me = this;
        sprite.stopAnimation();
        if (me.animating) {
            return sprite.animate(Ext.applyIf(attr, me.chart.animate));
        } else {
            me.animating = true;
            return sprite.animate(Ext.apply(Ext.applyIf(attr, me.chart.animate), {
                
                callback: function() {
                    me.animating = false;
                    me.fireEvent('afterrender', me);
                }
            }));
        }
    },

    
    getGutters: function() {
        return this.nullGutters;
    },

    
    getPadding: function() {
        return this.nullPadding;
    },

    
    onItemMouseOver: function(item) {
        var me = this;
        if (item.series === me) {
            if (me.highlight) {
                me.highlightItem(item);
            }
            if (me.tooltip) {
                me.showTip(item);
            }
        }
    },

    
    onItemMouseOut: function(item) {
        var me = this;
        if (item.series === me) {
            me.unHighlightItem();
            if (me.tooltip) {
                me.hideTip(item);
            }
        }
    },

    
    onMouseLeave: function() {
        var me = this;
        me.unHighlightItem();
        if (me.tooltip) {
            me.hideTip();
        }
    },

    
    getItemForPoint: function(x, y) {
        
        if (!this.items || !this.items.length || this.seriesIsHidden) {
            return null;
        }
        var me = this,
            items = me.items,
            bbox = me.bbox,
            item, i, ln;
        
        if (!Ext.draw.Draw.withinBox(x, y, bbox)) {
            return null;
        }
        for (i = 0, ln = items.length; i < ln; i++) {
            if (items[i] && this.isItemInPoint(x, y, items[i], i)) {
                return items[i];
            }
        }

        return null;
    },

    isItemInPoint: function(x, y, item, i) {
        return false;
    },

    
    hideAll: function() {
        var me = this,
            items = me.items,
            item, len, i, j, l, sprite, shadows;

        me.seriesIsHidden = true;
        me._prevShowMarkers = me.showMarkers;

        me.showMarkers = false;
        
        me.hideLabels(0);
        
        for (i = 0, len = items.length; i < len; i++) {
            item = items[i];
            sprite = item.sprite;
            if (sprite) {
                sprite.setAttributes({
                    hidden: true
                }, true);
            }

            if (sprite && sprite.shadows) {
                shadows = sprite.shadows;
                for (j = 0, l = shadows.length; j < l; ++j) {
                    shadows[j].setAttributes({
                        hidden: true
                    }, true);
                }
            }
        }
    },

    
    showAll: function() {
        var me = this,
            prevAnimate = me.chart.animate;
        me.chart.animate = false;
        me.seriesIsHidden = false;
        me.showMarkers = me._prevShowMarkers;
        me.drawSeries();
        me.chart.animate = prevAnimate;
    },
    
    hide: function() {
        if (this.items) {
            var me = this,
                items = me.items,
                i, j, lsh, ln, shadows;
            
            if (items && items.length) {
                for (i = 0, ln = items.length; i < ln; ++i) {
                    if (items[i].sprite) {
                        items[i].sprite.hide(true);

                        shadows = items[i].shadows || items[i].sprite.shadows;
                        if (shadows) {
                            for (j = 0, lsh = shadows.length; j < lsh; ++j) {
                                shadows[j].hide(true);
                            }
                        }
                    }
                }
                me.hideLabels();
            }
        }
    },

    
    getLegendColor: function(index) {
        var me = this, fill, stroke;
        if (me.seriesStyle) {
            fill = me.seriesStyle.fill;
            stroke = me.seriesStyle.stroke;
            if (fill && fill != 'none') {
                return fill;
            }
            if(stroke){
                return stroke;
            }
        }
        return (me.colorArrayStyle)?me.colorArrayStyle[me.themeIdx % me.colorArrayStyle.length]:'#000';
    },

    
    visibleInLegend: function(index){
        var excludes = this.__excludes;
        if (excludes) {
            return !excludes[index];
        }
        return !this.seriesIsHidden;
    },

    
    setTitle: function(index, title) {
        var me = this,
            oldTitle = me.title;

        if (Ext.isString(index)) {
            title = index;
            index = 0;
        }

        if (Ext.isArray(oldTitle)) {
            oldTitle[index] = title;
        } else {
            me.title = title;
        }

        me.fireEvent('titlechange', title, index);
    }
});


Ext.define('Ext.chart.series.Cartesian', {

    

    extend:  Ext.chart.series.Series ,

    alternateClassName: ['Ext.chart.CartesianSeries', 'Ext.chart.CartesianChart'],

    

    
    xField: null,

    
    yField: null,

    
    axis: 'left',

    getLegendLabels: function() {
        var me = this,
            labels = [],
            fields, i, ln,
            combinations = me.combinations,
            title,
            combo, label0, label1;

        fields = [].concat(me.yField);
        for (i = 0, ln = fields.length; i < ln; i++) {
            title = me.title;
            
            labels.push((Ext.isArray(title) ? title[i] : title) || fields[i]);
        }

        
        
        if (combinations) {
            combinations = Ext.Array.from(combinations);
            for (i = 0, ln = combinations.length; i < ln; i++) {
                combo = combinations[i];
                label0 = labels[combo[0]];
                label1 = labels[combo[1]];
                labels[combo[1]] = label0 + ' & ' + label1;
                labels.splice(combo[0], 1);
            }
        }

        return labels;
    },

    
    eachYValue: function(record, fn, scope) {
        var me = this,
            yValueAccessors = me.getYValueAccessors(),
            i, ln, accessor;
        
        for (i = 0, ln = yValueAccessors.length; i < ln; i++) {
            accessor = yValueAccessors[i];
            fn.call(scope, accessor(record), i);
        }
    },

    
    getYValueCount: function() {
        return this.getYValueAccessors().length;
    },

    combine: function(index1, index2) {
        var me = this,
            accessors = me.getYValueAccessors(),
            accessor1 = accessors[index1],
            accessor2 = accessors[index2];

        
        accessors[index2] = function(record) {
            return accessor1(record) + accessor2(record);
        };
        accessors.splice(index1, 1);

        me.callParent([index1, index2]);
    },

    clearCombinations: function() {
        
        delete this.yValueAccessors;
        this.callParent();
    },

    
    getYValueAccessors: function() {
        var me = this,
            accessors = me.yValueAccessors,
            yFields, i, ln;

        function getFieldAccessor(field) {
            return function(record) {
                return record.get(field);
            }
        }

        if (!accessors) {
            accessors = me.yValueAccessors = [];
            yFields = [].concat(me.yField);
            for (i = 0, ln = yFields.length; i < ln; i++) {
                accessors.push(getFieldAccessor(yFields[i]));
            }
        }
        return accessors;
    },

    
    getMinMaxXValues: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            data = store.data.items,
            count = me.getRecordCount(),
            i, ln, record,
            min, max,
            xField = me.xField,
            xValue;

        if (count > 0) {
            min = Infinity;
            max = -min;
                
            for (i = 0, ln = data.length; i < ln; i++) {
                record = data[i];
                xValue = record.get(xField);
                if (xValue > max) {
                    max = xValue;
                }
                if (xValue < min) {
                    min = xValue;
                }
            }
            
            
            if (min == Infinity) {
                min = 0;
            }
            
            if (max == -Infinity) {
                max = count - 1;
            }
        } else {
            min = max = 0;
        }
        return [min, max];
    },

    
    getMinMaxYValues: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            data = store.data.items,
            count = me.getRecordCount(),
            i, ln, record,
            stacked = me.stacked,
            min, max,
            positiveTotal, negativeTotal;

        function eachYValueStacked(yValue, i) {
            if (!me.isExcluded(i)) {
                if (yValue < 0) {
                    negativeTotal += yValue;
                } else {
                    positiveTotal += yValue;
                }
            }
        }

        function eachYValue(yValue, i) {
            if (!me.isExcluded(i)) {
                if (yValue > max) {
                    max = yValue;
                }
                if (yValue < min) {
                    min = yValue;
                }
            }
        }

        if (count > 0) {
            min = Infinity;
            max = -min;
            
            for (i = 0, ln = data.length; i < ln; i++) {
                record = data[i];
                if (stacked) {
                    positiveTotal = 0;
                    negativeTotal = 0;
                    me.eachYValue(record, eachYValueStacked);
                    if (positiveTotal > max) {
                        max = positiveTotal;
                    }
                    if (negativeTotal < min) {
                        min = negativeTotal;
                    }
                } else {
                    me.eachYValue(record, eachYValue);
                }
            }
            
            
            if (min == Infinity) {
                min = 0;
            }
            
            if (max == -Infinity) {
                max = count - 1;
            }
        } else {
            min = max = 0;
        }
        return [min, max];
    },

    getAxesForXAndYFields: function() {
        var me = this,
            axes = me.chart.axes,
            reverse = me.reverse,
            axis = [].concat(me.axis),
            yFields = {}, yFieldList = [].concat(me.yField),
            xFields = {}, xFieldList = [].concat(me.xField),
            fields, xAxis, yAxis, i, ln, flipXY;

        
        flipXY = me.type === 'bar' && me.column === false;
        if(flipXY) {
            fields = yFieldList;
            yFieldList = xFieldList;
            xFieldList = fields;
        }
        if (Ext.Array.indexOf(axis, 'top') > -1) {
            xAxis = 'top';
        } else if (Ext.Array.indexOf(axis, 'bottom') > -1) {
            xAxis = 'bottom';
        } else {
            if (axes.get('top') && axes.get('bottom')) {
                for (i = 0, ln = xFieldList.length; i < ln; i++) {
                    xFields[xFieldList[i]] = true;
                }
                fields = [].concat(axes.get('bottom').fields);
                for (i = 0, ln = fields.length; i < ln; i++) {
                    if (xFields[fields[i]]) {
                        xAxis = 'bottom';
                        break;
                    }
                }
                fields = [].concat(axes.get('top').fields);
                for (i = 0, ln = fields.length; i < ln; i++) {
                    if (xFields[fields[i]]) {
                        xAxis = 'top';
                        break;
                    }
                }
            } else if (axes.get('top')) {
                xAxis = 'top';
            } else if (axes.get('bottom')) {
                xAxis = 'bottom';
            }
        }
        
        if (Ext.Array.indexOf(axis, 'left') > -1) {
            yAxis = flipXY ? 'right' : 'left';
        } else if (Ext.Array.indexOf(axis, 'right') > -1) {
            yAxis = flipXY ? 'left' : 'right';
        } else {
            if (axes.get('left') && axes.get('right')) {
                for (i = 0, ln = yFieldList.length; i < ln; i++) {
                    yFields[yFieldList[i]] = true;
                }
                fields = [].concat(axes.get('right').fields);
                for (i = 0, ln = fields.length; i < ln; i++) {
                    if (yFields[fields[i]]) {
                        break;
                    }
                }
                fields = [].concat(axes.get('left').fields);
                for (i = 0, ln = fields.length; i < ln; i++) {
                    if (yFields[fields[i]]) {
                        yAxis = 'left';
                        break;
                    }
                }
            } else if (axes.get('left')) {
                yAxis = 'left';
            } else if (axes.get('right')) {
                yAxis = 'right';
            }
        }

        return flipXY ? {
            xAxis: yAxis,
            yAxis: xAxis
        }: {
            xAxis: xAxis,
            yAxis: yAxis
        };
    }


});


Ext.define('Ext.chart.series.Area', {

    

    extend:  Ext.chart.series.Cartesian ,

    alias: 'series.area',

                                                                       

    

    type: 'area',

    
    stacked: true,

    
    style: {},

    constructor: function(config) {
        this.callParent(arguments);
        var me = this,
            surface = me.chart.surface,
            i, l;
        config.highlightCfg = Ext.Object.merge({}, {
            lineWidth: 3,
            stroke: '#55c',
            opacity: 0.8,
            color: '#f00'
        }, config.highlightCfg);

        Ext.apply(me, config, {
            __excludes: []
        });
        if (me.highlight) {
            me.highlightSprite = surface.add({
                type: 'path',
                path: ['M', 0, 0],
                zIndex: 1000,
                opacity: 0.3,
                lineWidth: 5,
                hidden: true,
                stroke: '#444'
            });
        }
        me.group = surface.getGroup(me.seriesId);
    },

    
    shrink: function(xValues, yValues, size) {
        var len = xValues.length,
            ratio = Math.floor(len / size),
            i, j,
            xSum = 0,
            yCompLen = this.areas.length,
            ySum = [],
            xRes = [],
            yRes = [];
        
        for (j = 0; j < yCompLen; ++j) {
            ySum[j] = 0;
        }
        for (i = 0; i < len; ++i) {
            xSum += +xValues[i];
            for (j = 0; j < yCompLen; ++j) {
                ySum[j] += +yValues[i][j];
            }
            if (i % ratio == 0) {
                
                xRes.push(xSum/ratio);
                for (j = 0; j < yCompLen; ++j) {
                    ySum[j] /= ratio;
                }
                yRes.push(ySum);
                
                xSum = 0;
                for (j = 0, ySum = []; j < yCompLen; ++j) {
                    ySum[j] = 0;
                }
            }
        }
        return {
            x: xRes,
            y: yRes
        };
    },

    
    getBounds: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            data = store.data.items,
            i, l, record,
            areas = [].concat(me.yField),
            areasLen = areas.length,
            xValues = [],
            yValues = [],
            infinity = Infinity,
            minX = infinity,
            minY = infinity,
            maxX = -infinity,
            maxY = -infinity,
            math = Math,
            mmin = math.min,
            mmax = math.max,
            boundAxis = me.getAxesForXAndYFields(),
            boundXAxis = boundAxis.xAxis,
            boundYAxis = boundAxis.yAxis,
            ends, allowDate, tmp,
            bbox, xScale, yScale, xValue, yValue, areaIndex, acumY, ln, sumValues, clipBox, areaElem, axis, out;

        me.setBBox();
        bbox = me.bbox;

        if (axis = chart.axes.get(boundXAxis)) {
            if (axis.type === 'Time') {
                allowDate = true;
            }
            ends = axis.applyData();
            minX = ends.from;
            maxX = ends.to;
        }

        if (axis = chart.axes.get(boundYAxis)) {
            ends = axis.applyData();
            minY = ends.from;
            maxY = ends.to;
        }

        
        if (me.xField && !Ext.isNumber(minX)) {
            axis = me.getMinMaxXValues();
            allowDate = true;
            minX = axis[0];
            maxX = axis[1];
        }

        if (me.yField && !Ext.isNumber(minY)) {
            axis = me.getMinMaxYValues();
            minY = axis[0];
            maxY = axis[1];
        }

        if (!Ext.isNumber(minY)) {
            minY = 0;
        }
        if (!Ext.isNumber(maxY)) {
            maxY = 0;
        }

        l = data.length;
        if (l > 0 && allowDate) {
            tmp = data[0].get(me.xField);
            if (typeof tmp != 'number') {
                tmp = +tmp;
                if (isNaN(tmp)) {
                    allowDate = false;
                }
            } 
        }
        for (i = 0; i < l; i++) {
            record = data[i];
            xValue = record.get(me.xField);
            yValue = [];
            if (typeof xValue != 'number') {
                if (allowDate) {
                    xValue = +xValue;
                } else {
                    xValue = i;
                }
            }
            xValues.push(xValue);
            acumY = 0;
            for (areaIndex = 0; areaIndex < areasLen; areaIndex++) {
                
                if (me.__excludes[areaIndex]) {
                    continue;
                }
                areaElem = record.get(areas[areaIndex]);
                if (typeof areaElem == 'number') {
                    yValue.push(areaElem);
                }
            }
            yValues.push(yValue);
        }

        xScale = bbox.width / ((maxX - minX) || 1);
        yScale = bbox.height / ((maxY - minY) || 1);

        ln = xValues.length;
        if ((ln > bbox.width) && me.areas) {
            sumValues = me.shrink(xValues, yValues, bbox.width);
            xValues = sumValues.x;
            yValues = sumValues.y;
        }

        return {
            bbox: bbox,
            minX: minX,
            minY: minY,
            xValues: xValues,
            yValues: yValues,
            xScale: xScale,
            yScale: yScale,
            areasLen: areasLen
        };
    },

    
    getPaths: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            first = true,
            bounds = me.getBounds(),
            bbox = bounds.bbox,
            items = me.items = [],
            componentPaths = [],
            componentPath,
            count = 0,
            paths = [],
            reverse = me.reverse,
            i, ln, x, y, xValue, yValue, acumY, areaIndex, 
            prevAreaIndex, areaElem, path, startX, idx;

        ln = bounds.xValues.length;
        
        for (i = 0; i < ln; i++) {
            xValue = bounds.xValues[i];
            idx = reverse ? ln - i - 1 : i;
            yValue = bounds.yValues[idx];
            x = bbox.x + (xValue - bounds.minX) * bounds.xScale;
            if (startX === undefined) {
                startX = x;
            }
            acumY = 0;
            count = 0;
            for (areaIndex = 0; areaIndex < bounds.areasLen; areaIndex++) {
                
                if (me.__excludes[areaIndex]) {
                    continue;
                }
                if (!componentPaths[areaIndex]) {
                    componentPaths[areaIndex] = [];
                }
                areaElem = yValue[count];
                acumY += areaElem;
                y = bbox.y + bbox.height - (acumY - bounds.minY) * bounds.yScale;
                if (!paths[areaIndex]) {
                    paths[areaIndex] = ['M', x, y];
                    componentPaths[areaIndex].push(['L', x, y]);
                } else {
                    paths[areaIndex].push('L', x, y);
                    componentPaths[areaIndex].push(['L', x, y]);
                }
                if (!items[areaIndex]) {
                    items[areaIndex] = {
                        pointsUp: [],
                        pointsDown: [],
                        series: me
                    };
                }
                items[areaIndex].pointsUp.push([x, y]);
                count++;
            }
        }

        
        for (areaIndex = 0; areaIndex < bounds.areasLen; areaIndex++) {
            
            if (me.__excludes[areaIndex]) {
                continue;
            }
            path = paths[areaIndex];

            
            if (areaIndex == 0 || first) {
                first = false;

                path.push('L', x, bbox.y + bbox.height,
                          'L', startX, bbox.y + bbox.height,
                          'Z');
            }
            
            else {
                componentPath = componentPaths[prevAreaIndex];
                componentPath.reverse();
                path.push('L', x, componentPath[0][2]);
                for (i = 0; i < ln; i++) {
                    path.push(componentPath[i][0],
                              componentPath[i][1],
                              componentPath[i][2]);
                    items[areaIndex].pointsDown[ln -i -1] = [componentPath[i][1], componentPath[i][2]];
                }
                path.push('L', startX, path[2], 'Z');
            }
            prevAreaIndex = areaIndex;
        }
        return {
            paths: paths,
            areasLen: bounds.areasLen
        };
    },

    
    drawSeries: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            surface = chart.surface,
            animate = chart.animate,
            group = me.group,
            endLineStyle = Ext.apply(me.seriesStyle, me.style),
            colorArrayStyle = me.colorArrayStyle,
            colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0,
            themeIndex = me.themeIdx,
            areaIndex, areaElem, paths, path, rendererAttributes, idx;
        
        me.unHighlightItem();
        me.cleanHighlights();

        if (!store || !store.getCount() || me.seriesIsHidden) {
            me.hide();
            me.items = [];
            return;
        }

        paths = me.getPaths();

        if (!me.areas) {
            me.areas = [];
        }

        for (areaIndex = 0; areaIndex < paths.areasLen; areaIndex++) {
            
            if (me.__excludes[areaIndex]) {
                continue;
            }
            idx = themeIndex + areaIndex;
            if (!me.areas[areaIndex]) {
                me.items[areaIndex].sprite = me.areas[areaIndex] = surface.add(Ext.apply({}, {
                    type: 'path',
                    group: group,
                    
                    path: paths.paths[areaIndex],
                    stroke: endLineStyle.stroke || colorArrayStyle[idx % colorArrayLength],
                    fill: colorArrayStyle[idx % colorArrayLength]
                }, endLineStyle || {}));
            }
            areaElem = me.areas[areaIndex];
            path = paths.paths[areaIndex];
            if (animate) {
                
                rendererAttributes = me.renderer(areaElem, false, {
                    path: path,
                    
                    fill: colorArrayStyle[areaIndex % colorArrayLength],
                    stroke: endLineStyle.stroke || colorArrayStyle[areaIndex % colorArrayLength]
                }, areaIndex, store);
                
                me.animation = me.onAnimate(areaElem, {
                    to: rendererAttributes
                });
            } else {
                rendererAttributes = me.renderer(areaElem, false, {
                    path: path,
                    
                    hidden: false,
                    fill: colorArrayStyle[idx % colorArrayLength],
                    stroke: endLineStyle.stroke || colorArrayStyle[idx % colorArrayLength]
                }, areaIndex, store);
                me.areas[areaIndex].setAttributes(rendererAttributes, true);
            }
        }
        me.renderLabels();
        me.renderCallouts();
    },

    
    onAnimate: function(sprite, attr) {
        sprite.show();
        return this.callParent(arguments);
    },

    
    onCreateLabel: function(storeItem, item, i, display) {
        
        
        
        
        
        
        
        
        
        return null;

        var me = this,
            group = me.labelsGroup,
            config = me.label,
            bbox = me.bbox,
            endLabelStyle = Ext.apply({}, config, me.seriesLabelStyle || {});

        return me.chart.surface.add(Ext.apply({
            'type': 'text',
            'text-anchor': 'middle',
            'group': group,
            'x': Number(item.point[0]),
            'y': bbox.y + bbox.height / 2
        }, endLabelStyle || {}));
    },

    
    onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {
        var me = this,
            chart = me.chart,
            resizing = chart.resizing,
            config = me.label,
            format = config.renderer,
            field = config.field,
            bbox = me.bbox,
            x = Number(item.point[i][0]),
            y = Number(item.point[i][1]),
            labelBox, width, height;

        label.setAttributes({
            text: format(storeItem.get(field[index]), label, storeItem, item, i, display, animate, index),
            hidden: true
        }, true);

        labelBox = label.getBBox();
        width = labelBox.width / 2;
        height = labelBox.height / 2;

        
        if (x < bbox.x + width) {
            x = bbox.x + width;
        } else if (x + width > bbox.x + bbox.width) {
            x = bbox.x + bbox.width - width;
        }

        y = y - height;
        if (y < bbox.y + height) {
            y += 2 * height;
        } else if (y + height > bbox.y + bbox.height) {
            y -= 2 * height;
        }

        if (me.chart.animate && !me.chart.resizing) {
            label.show(true);
            me.onAnimate(label, {
                to: {
                    x: x,
                    y: y
                }
            });
        } else {
            label.setAttributes({
                x: x,
                y: y
            }, true);
            if (resizing && me.animation) {
                me.animation.on('afteranimate', function() {
                    label.show(true);
                });
            } else {
                label.show(true);
            }
        }
    },

    
    onPlaceCallout : function(callout, storeItem, item, i, display, animate, index) {
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            resizing = chart.resizing,
            config = me.callouts,
            items = me.items,
            prev = (i == 0) ? false : items[i -1].point,
            next = (i == items.length -1) ? false : items[i +1].point,
            cur = item.point,
            dir, norm, normal, a, aprev, anext,
            bbox = (callout && callout.label ? callout.label.getBBox() : {width:0,height:0}),
            offsetFromViz = 30,
            offsetToSide = 10,
            offsetBox = 3,
            boxx, boxy, boxw, boxh,
            p, clipRect = me.clipRect,
            x, y;

        if (!bbox.width || !bbox.height) {
            return;
        }

        
        if (!prev) {
            prev = cur;
        }
        if (!next) {
            next = cur;
        }
        a = (next[1] - prev[1]) / (next[0] - prev[0]);
        aprev = (cur[1] - prev[1]) / (cur[0] - prev[0]);
        anext = (next[1] - cur[1]) / (next[0] - cur[0]);

        norm = Math.sqrt(1 + a * a);
        dir = [1 / norm, a / norm];
        normal = [-dir[1], dir[0]];

        
        if (aprev > 0 && anext < 0 && normal[1] < 0 || aprev < 0 && anext > 0 && normal[1] > 0) {
            normal[0] *= -1;
            normal[1] *= -1;
        } else if (Math.abs(aprev) < Math.abs(anext) && normal[0] < 0 || Math.abs(aprev) > Math.abs(anext) && normal[0] > 0) {
            normal[0] *= -1;
            normal[1] *= -1;
        }

        
        x = cur[0] + normal[0] * offsetFromViz;
        y = cur[1] + normal[1] * offsetFromViz;

        
        boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
        boxy = y - bbox.height /2 - offsetBox;
        boxw = bbox.width + 2 * offsetBox;
        boxh = bbox.height + 2 * offsetBox;

        
        
        if (boxx < clipRect[0] || (boxx + boxw) > (clipRect[0] + clipRect[2])) {
            normal[0] *= -1;
        }
        if (boxy < clipRect[1] || (boxy + boxh) > (clipRect[1] + clipRect[3])) {
            normal[1] *= -1;
        }

        
        x = cur[0] + normal[0] * offsetFromViz;
        y = cur[1] + normal[1] * offsetFromViz;

        
        boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
        boxy = y - bbox.height /2 - offsetBox;
        boxw = bbox.width + 2 * offsetBox;
        boxh = bbox.height + 2 * offsetBox;

        
        callout.lines.setAttributes({
            path: ["M", cur[0], cur[1], "L", x, y, "Z"]
        }, true);
        
        callout.box.setAttributes({
            x: boxx,
            y: boxy,
            width: boxw,
            height: boxh
        }, true);
        
        callout.label.setAttributes({
            x: x + (normal[0] > 0? offsetBox : -(bbox.width + offsetBox)),
            y: y
        }, true);
        for (p in callout) {
            callout[p].show(true);
        }
    },

    isItemInPoint: function(x, y, item, i) {
        var me = this,
            pointsUp = item.pointsUp,
            pointsDown = item.pointsDown,
            abs = Math.abs,
            distChanged = false,
            last = false,
            reverse = me.reverse,
            dist = Infinity, p, pln, point;

        for (p = 0, pln = pointsUp.length; p < pln; p++) {
            point = [pointsUp[p][0], pointsUp[p][1]];
            
            distChanged = false;
            last = p == pln -1;

            if (dist > abs(x - point[0])) {
                dist = abs(x - point[0]);
                distChanged = true;
                if (last) {
                    ++p;
                }
            }
            
            if (!distChanged || (distChanged && last)) {
                point = pointsUp[p -1];
                if (y >= point[1] && (!pointsDown.length || y <= (pointsDown[p -1][1]))) {
                    idx = reverse ? pln - p : p - 1;
                    item.storeIndex = idx;
                    item.storeField = me.yField[i];
                    item.storeItem = me.chart.getChartStore().getAt(idx);
                    item._points = pointsDown.length? [point, pointsDown[p - 1]] : [point];
                    return true;
                } else {
                    break;
                }
            }
        }
        return false;
    },

    
    highlightSeries: function() {
        var area, to, fillColor;
        if (this._index !== undefined) {
            area = this.areas[this._index];
            if (area.__highlightAnim) {
                area.__highlightAnim.paused = true;
            }
            area.__highlighted = true;
            area.__prevOpacity = area.__prevOpacity || area.attr.opacity || 1;
            area.__prevFill = area.__prevFill || area.attr.fill;
            area.__prevLineWidth = area.__prevLineWidth || area.attr.lineWidth;
            fillColor = Ext.draw.Color.fromString(area.__prevFill);
            to = {
                lineWidth: (area.__prevLineWidth || 0) + 2
            };
            if (fillColor) {
                to.fill = fillColor.getLighter(0.2).toString();
            }
            else {
                to.opacity = Math.max(area.__prevOpacity - 0.3, 0);
            }
            if (this.chart.animate) {
                area.__highlightAnim = new Ext.fx.Anim(Ext.apply({
                    target: area,
                    to: to
                }, this.chart.animate));
            }
            else {
                area.setAttributes(to, true);
            }
        }
    },

    
    unHighlightSeries: function() {
        var area;
        if (this._index !== undefined) {
            area = this.areas[this._index];
            if (area.__highlightAnim) {
                area.__highlightAnim.paused = true;
            }
            if (area.__highlighted) {
                area.__highlighted = false;
                area.__highlightAnim = new Ext.fx.Anim({
                    target: area,
                    to: {
                        fill: area.__prevFill,
                        opacity: area.__prevOpacity,
                        lineWidth: area.__prevLineWidth
                    }
                });
            }
        }
    },

    
    highlightItem: function(item) {
        var me = this,
            points, path;
        if (!item) {
            this.highlightSeries();
            return;
        }
        
        points = item._points;
        if (points.length === 2) {
            path = ['M', points[0][0], points[0][1], 'L', points[1][0], points[1][1]];
        } else {
            path = ['M', points[0][0], points[0][1], 'L', points[0][0], me.bbox.y + me.bbox.height];
        }
        
        me.highlightSprite.setAttributes({
            path: path,
            hidden: false
        }, true);
    },

    
    unHighlightItem: function(item) {
        if (!item) {
            this.unHighlightSeries();
        }

        if (this.highlightSprite) {
            this.highlightSprite.hide(true);
        }
    },

    
    hideAll: function(index) {
        var me = this;
        index = (isNaN(me._index) ? index : me._index) || 0;
        me.__excludes[index] = true;
        me.areas[index].hide(true);
        me.redraw();
    },

    
    showAll: function(index) {
        var me = this;
        index = (isNaN(me._index) ? index : me._index) || 0;
        me.__excludes[index] = false;
        me.areas[index].show(true);
        me.redraw();
    },

    redraw: function() {
        
        
        
        var me = this,
            prevLegendConfig;
        prevLegendConfig = me.chart.legend.rebuild;
        me.chart.legend.rebuild = false;
        me.chart.redraw();
        me.chart.legend.rebuild = prevLegendConfig;
    },
    
    hide: function() {
        if (this.areas) {
            var me = this,
                areas = me.areas,
                i, j, l, ln, shadows;
            
            if (areas && areas.length) {
                for (i = 0, ln = areas.length; i < ln; ++i) {
                    if (areas[i]) {
                        areas[i].hide(true);
                    }
                }
                me.hideLabels();
            }
        }
    },

    
    getLegendColor: function(index) {
        var me = this;
        index += me.themeIdx;
        return me.colorArrayStyle[index % me.colorArrayStyle.length];
    }
});


Ext.define('Ext.chart.series.Bar', {

    

    extend:  Ext.chart.series.Cartesian ,

    alternateClassName: ['Ext.chart.BarSeries', 'Ext.chart.BarChart', 'Ext.chart.StackedBarChart'],

                                                     

    

    type: 'bar',

    alias: 'series.bar',
    
    column: false,

    
    style: {},

    
    gutter: 38.2,

    
    groupGutter: 38.2,

    
    xPadding: 0,

    
    yPadding: 10,

    
    
    defaultRotate: {
        x: 0,
        y: 0,
        degrees: 0
    },

    constructor: function(config) {
        this.callParent(arguments);
        var me = this,
            surface = me.chart.surface,
            shadow = me.chart.shadow,
            i, l;
        config.highlightCfg = Ext.Object.merge({
            lineWidth: 3,
            stroke: '#55c',
            opacity: 0.8,
            color: '#f00'
        }, config.highlightCfg);
        Ext.apply(me, config, {
            shadowAttributes: [{
                "stroke-width": 6,
                "stroke-opacity": 0.05,
                stroke: 'rgb(200, 200, 200)',
                translate: {
                    x: 1.2,
                    y: 1.2
                }
            }, {
                "stroke-width": 4,
                "stroke-opacity": 0.1,
                stroke: 'rgb(150, 150, 150)',
                translate: {
                    x: 0.9,
                    y: 0.9
                }
            }, {
                "stroke-width": 2,
                "stroke-opacity": 0.15,
                stroke: 'rgb(100, 100, 100)',
                translate: {
                    x: 0.6,
                    y: 0.6
                }
            }]
        });

        me.group = surface.getGroup(me.seriesId + '-bars');
        if (shadow) {
            for (i = 0, l = me.shadowAttributes.length; i < l; i++) {
                me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
            }
        }
    },

    
    getPadding: function() {
        var me = this,
            xPadding = me.xPadding,
            yPadding = me.yPadding,
            padding = { };

        if (Ext.isNumber(xPadding)) {
            padding.left = xPadding;
            padding.right = xPadding;
        } else if (Ext.isObject(xPadding)) {
            padding.left = xPadding.left;
            padding.right = xPadding.right;
        } else {
            padding.left = 0;
            padding.right = 0;
        }
        padding.width = padding.left + padding.right;

        if (Ext.isNumber(yPadding)) {
            padding.bottom = yPadding;
            padding.top = yPadding;
        } else if (Ext.isObject(yPadding)) {
            padding.bottom = yPadding.bottom;
            padding.top = yPadding.top;
        } else {
            padding.bottom = 0;
            padding.top = 0;
        }
        padding.height = padding.bottom + padding.top;

        return padding;
    },

    
    getBarGirth: function() {
        var me = this,
            store = me.chart.getChartStore(),
            column = me.column,
            ln = store.getCount(),
            gutter = me.gutter / 100,
            padding,
            property;

        property = (column ? 'width' : 'height');
        
        if (me.style && me.style[property]) {
            me.configuredColumnGirth = true;
            return +me.style[property];
        }
        
        padding = me.getPadding();
        
        return (me.chart.chartBBox[property] - padding[property]) / (ln * (gutter + 1) - gutter);
    },

    
    getGutters: function() {
        var me = this,
            column = me.column,
            padding = me.getPadding(),
            halfBarGirth = me.getBarGirth() / 2,
            lowerGutter = Math.ceil((column ? padding.left : padding.bottom) + halfBarGirth),
            upperGutter = Math.ceil((column ? padding.right : padding.top) + halfBarGirth);

        return {
            lower: lowerGutter,
            upper: upperGutter,
            verticalAxis: !column
        };
    },

    
    getBounds: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            data = store.data.items,
            i, ln, record,
            bars = [].concat(me.yField),
            barsLoc = [],
            barsLen = bars.length,
            groupBarsLen = barsLen,
            groupGutter = me.groupGutter / 100,
            column = me.column,
            padding = me.getPadding(),
            stacked = me.stacked,
            barWidth = me.getBarGirth(),
            barWidthProperty = column ? 'width' : 'height',
            math = Math,
            mmin = math.min,
            mmax = math.max,
            mabs = math.abs,
            boundAxes = me.getAxesForXAndYFields(),
            boundYAxis = boundAxes.yAxis,
            minX, maxX, colsScale, colsZero, gutters,
            ends, shrunkBarWidth, groupBarWidth, bbox, minY, maxY, axis, out,
            scale, zero, total, rec, j, plus, minus, inflections, tick, loc;

        me.setBBox(true);
        bbox = me.bbox;

        
        if (me.__excludes) {
            for (j = 0, total = me.__excludes.length; j < total; j++) {
                if (me.__excludes[j]) {
                    groupBarsLen--;
                }
            }
        }
        axis = chart.axes.get(boundYAxis);
        if (axis) {
            ends = axis.applyData();
            minY = ends.from;
            maxY = ends.to;
        }

        if (me.yField && !Ext.isNumber(minY)) {
            out = me.getMinMaxYValues();
            minY = out[0];
            maxY = out[1];
        }

        if (!Ext.isNumber(minY)) {
            minY = 0;
        }
        if (!Ext.isNumber(maxY)) {
            maxY = 0;
        }
        scale = (column ? bbox.height - padding.height : bbox.width - padding.width) / (maxY - minY);
        shrunkBarWidth = barWidth;
        groupBarWidth = (barWidth / ((stacked ? 1 : groupBarsLen) * (groupGutter + 1) - groupGutter));
        
        if (barWidthProperty in me.style) {
            groupBarWidth = mmin(groupBarWidth, me.style[barWidthProperty]);
            shrunkBarWidth = groupBarWidth * ((stacked ? 1 : groupBarsLen) * (groupGutter + 1) - groupGutter);
        }
        zero = (column) ? bbox.y + bbox.height - padding.bottom : bbox.x + padding.left;

        if (stacked) {
            total = [[], []];
            for (i = 0, ln = data.length; i < ln; i++) {
                record = data[i];
                total[0][i] = total[0][i] || 0;
                total[1][i] = total[1][i] || 0;
                for (j = 0; j < barsLen; j++) {
                    if (me.__excludes && me.__excludes[j]) {
                        continue;
                    }
                    rec = record.get(bars[j]);
                    total[+(rec > 0)][i] += mabs(rec);
                }
            }
            total[+(maxY > 0)].push(mabs(maxY));
            total[+(minY > 0)].push(mabs(minY));
            minus = mmax.apply(math, total[0]);
            plus = mmax.apply(math, total[1]);
            scale = (column ? bbox.height - padding.height : bbox.width - padding.width) / (plus + minus);
            zero = zero + minus * scale * (column ? -1 : 1);
        }
        else if (minY / maxY < 0) {
            zero = zero - minY * scale * (column ? -1 : 1);
        }

        
        if (me.boundColumn) {
            axis = chart.axes.get(boundAxes.xAxis);
            if (axis) {
                ends = axis.applyData();
                minX = ends.from;
                maxX = ends.to;
            }
            if (me.xField && !Ext.isNumber(minX)) {
                out = me.getMinMaxYValues();
                minX = out[0];
                maxX = out[1];
            }
            if (!Ext.isNumber(minX)) {
                minX = 0;
            }
            if (!Ext.isNumber(maxX)) {
                maxX = 0;
            }
            gutters = me.getGutters();
            colsScale = (bbox.width - (gutters.lower + gutters.upper)) / ((maxX - minX) || 1);

            colsZero = bbox.x + gutters.lower;
        
            barsLoc = [];
            for (i = 0, ln = data.length; i < ln; i++) {
                record = data[i];
                rec = record.get(me.xField);
                barsLoc[i] = colsZero + (rec - minX) * colsScale - (groupBarWidth / 2);
            }
        }
        
        
        else if (me.configuredColumnGirth) {
            axis = chart.axes.get(boundAxes.xAxis);
            if (axis) {
                inflections = axis.inflections;
                if (axis.isCategoryAxis || inflections.length == data.length) {
                    barsLoc = [];
                    for (i = 0, ln = data.length; i < ln; i++) {
                        tick = inflections[i];
                        loc = column ? tick[0] : tick[1];
                        barsLoc[i] = loc - (shrunkBarWidth / 2);
                    }                
                }                
            }
        }

        return {
            bars: bars,
            barsLoc: barsLoc,
            bbox: bbox,
            shrunkBarWidth: shrunkBarWidth,
            barsLen: barsLen,
            groupBarsLen: groupBarsLen,
            barWidth: barWidth,
            groupBarWidth: groupBarWidth,
            scale: scale,
            zero: zero,
            padding: padding,
            signed: minY / maxY < 0,
            minY: minY,
            maxY: maxY
        };
    },

    
    getPaths: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            data = store.data.items,
            i, total, record,
            bounds = me.bounds = me.getBounds(),
            items = me.items = [],
            yFields = Ext.isArray(me.yField) ? me.yField : [me.yField],
            gutter = me.gutter / 100,
            groupGutter = me.groupGutter / 100,
            animate = chart.animate,
            column = me.column,
            group = me.group,
            enableShadows = chart.shadow,
            shadowGroups = me.shadowGroups,
            shadowAttributes = me.shadowAttributes,
            shadowGroupsLn = shadowGroups.length,
            bbox = bounds.bbox,
            barWidth = bounds.barWidth,
            shrunkBarWidth = bounds.shrunkBarWidth,
            padding = me.getPadding(),
            stacked = me.stacked,
            barsLen = bounds.barsLen,
            colors = me.colorArrayStyle,
            colorLength = colors && colors.length || 0,
            themeIndex = me.themeIdx,
            reverse = me.reverse,
            math = Math,
            mmax = math.max,
            mmin = math.min,
            mabs = math.abs,
            j, yValue, height, totalDim, totalNegDim, bottom, top, hasShadow, barAttr, attrs, counter,
            totalPositiveValues, totalNegativeValues,
            shadowIndex, shadow, sprite, offset, floorY, idx, itemIdx, xPos, yPos, width, usedWidth, barIdx;

        for (i = 0, total = data.length; i < total; i++) {
            record = data[i];
            bottom = bounds.zero;
            top = bounds.zero;
            totalDim = 0;
            totalNegDim = 0;
            totalPositiveValues = totalNegativeValues = 0;
            hasShadow = false;
            usedWidth = 0;
            
            for (j = 0, counter = 0; j < barsLen; j++) {
                
                if (me.__excludes && me.__excludes[j]) {
                    continue;
                }
                yValue = record.get(bounds.bars[j]);
                if (yValue >= 0) {
                    totalPositiveValues += yValue;
                } else {
                    totalNegativeValues += yValue;
                }
                height = Math.round((yValue - mmax(bounds.minY, 0)) * bounds.scale);
                idx = themeIndex + (barsLen > 1 ? j : 0);
                barAttr = {
                    fill: colors[idx % colorLength]
                };
                
                if (column) {
                    idx = reverse ? (total - i - 1) : i;
                    barIdx = reverse ? (barsLen - counter - 1)  : counter;

                    if (me.boundColumn) {
                        xPos = bounds.barsLoc[idx];
                    }
                    else if (me.configuredColumnGirth && bounds.barsLoc.length) {
                        xPos = bounds.barsLoc[idx] + 
                               barIdx * bounds.groupBarWidth *
                               (1 + groupGutter) * !stacked;
                             
                    }
                    else {
                        xPos = bbox.x + padding.left +
                              (barWidth - shrunkBarWidth) * 0.5 +
                              idx * barWidth * (1 + gutter) +
                              barIdx * bounds.groupBarWidth *
                              (1 + groupGutter) * !stacked;
                    }
                    
                    Ext.apply(barAttr, {
                        height: height,
                        width: mmax(bounds.groupBarWidth, 0),
                        x: xPos,
                        y: bottom - height
                    });
                }
                else {
                    
                    offset = (total - 1) - i;
                    width = height + (bottom == bounds.zero);
                    xPos = bottom + (bottom != bounds.zero);

                    if (reverse) {
                        
                        xPos = bounds.zero + bbox.width - width - (usedWidth === 0 ? 1 : 0);
                        if (stacked) {
                            xPos -= usedWidth;
                            usedWidth += width;
                        }
                    }

                    if (me.configuredColumnGirth && bounds.barsLoc.length) {
                        yPos = bounds.barsLoc[i] +
                               counter * bounds.groupBarWidth * (1 + groupGutter) * !stacked;
                    }
                    else {
                        yPos = bbox.y + padding.top +
                               (barWidth - shrunkBarWidth) * 0.5 +
                               offset * barWidth * (1 + gutter) +
                               counter * bounds.groupBarWidth *
                               (1 + groupGutter) * !stacked + 1;
                    }
                    
                    Ext.apply(barAttr, {
                        height: mmax(bounds.groupBarWidth, 0),
                        width: width,
                        x: xPos,
                        y: yPos
                    });
                }
                if (height < 0) {
                    if (column) {
                        barAttr.y = top;
                        barAttr.height = mabs(height);
                    } else {
                        barAttr.x = top + height;
                        barAttr.width = mabs(height);
                    }
                }
                if (stacked) {
                    if (height < 0) {
                        top += height * (column ? -1 : 1);
                    } else {
                        bottom += height * (column ? -1 : 1);
                    }
                    totalDim += mabs(height);
                    if (height < 0) {
                        totalNegDim += mabs(height);
                    }
                }
                barAttr.x = Math.floor(barAttr.x) + 1;
                floorY = Math.floor(barAttr.y);
                if (Ext.isIE8m && barAttr.y > floorY) {
                    floorY--;
                }
                barAttr.y = floorY;
                barAttr.width = Math.floor(barAttr.width);
                barAttr.height = Math.floor(barAttr.height);
                items.push({
                    series: me,
                    yField: yFields[j],
                    storeItem: record,
                    value: [record.get(me.xField), yValue],
                    attr: barAttr,
                    point: column ? [barAttr.x + barAttr.width / 2, yValue >= 0 ? barAttr.y : barAttr.y + barAttr.height] :
                                    [yValue >= 0 ? barAttr.x + barAttr.width : barAttr.x, barAttr.y + barAttr.height / 2]
                });
                
                if (animate && chart.resizing) {
                    attrs = column ? {
                        x: barAttr.x,
                        y: bounds.zero,
                        width: barAttr.width,
                        height: 0
                    } : {
                        x: bounds.zero,
                        y: barAttr.y,
                        width: 0,
                        height: barAttr.height
                    };
                    if (enableShadows && (stacked && !hasShadow || !stacked)) {
                        hasShadow = true;
                        
                        for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) {
                            shadow = shadowGroups[shadowIndex].getAt(stacked ? i : (i * barsLen + j));
                            if (shadow) {
                                shadow.setAttributes(attrs, true);
                            }
                        }
                    }
                    
                    sprite = group.getAt(i * barsLen + j);
                    if (sprite) {
                        sprite.setAttributes(attrs, true);
                    }
                }
                counter++;
            }
            if (stacked && items.length) {
                items[i * counter].totalDim = totalDim;
                items[i * counter].totalNegDim = totalNegDim;
                items[i * counter].totalPositiveValues = totalPositiveValues;
                items[i * counter].totalNegativeValues = totalNegativeValues;
            }
        }
        if (stacked && counter == 0) {
            
            for (i = 0, total = data.length; i < total; i++) {
                for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) {
                    shadow = shadowGroups[shadowIndex].getAt(i);
                    if (shadow) {
                        shadow.hide(true);
                    }
                }
            }
        }
    },

    
    renderShadows: function(i, barAttr, baseAttrs, bounds) {
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            animate = chart.animate,
            stacked = me.stacked,
            shadowGroups = me.shadowGroups,
            shadowAttributes = me.shadowAttributes,
            shadowGroupsLn = shadowGroups.length,
            store = chart.getChartStore(),
            column = me.column,
            items = me.items,
            shadows = [],
            reverse = me.reverse,
            zero = bounds.zero,
            shadowIndex, shadowBarAttr, shadow, totalDim, totalNegDim, j, rendererAttributes;

        if ((stacked && (i % bounds.groupBarsLen === 0)) || !stacked) {
            j = i / bounds.groupBarsLen;
            
            for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) {
                shadowBarAttr = Ext.apply({}, shadowAttributes[shadowIndex]);
                shadow = shadowGroups[shadowIndex].getAt(stacked ? j : i);
                Ext.copyTo(shadowBarAttr, barAttr, 'x,y,width,height');
                if (!shadow) {
                    shadow = surface.add(Ext.apply({
                        type: 'rect',
                        group: shadowGroups[shadowIndex]
                    }, Ext.apply({}, baseAttrs, shadowBarAttr)));
                }
                if (stacked) {
                    totalDim = items[i].totalDim;
                    totalNegDim = items[i].totalNegDim;
                    if (column) {
                        shadowBarAttr.y = zero + totalNegDim - totalDim - 1;
                        shadowBarAttr.height = totalDim;
                    } else {
                        if (reverse) {
                            shadowBarAttr.x = zero + bounds.bbox.width - totalDim;
                        } else {
                            shadowBarAttr.x = zero - totalNegDim;
                        }
                        shadowBarAttr.width = totalDim;
                    }
                }
                
                rendererAttributes = me.renderer(shadow, store.getAt(j), shadowBarAttr, i, store);
                rendererAttributes.hidden = !!barAttr.hidden;
                if (animate) {
                    me.onAnimate(shadow, {
                        zero: bounds.zero + bounds.bbox.width, 
                        to: rendererAttributes 
                    });
                }
                else {
                    shadow.setAttributes(rendererAttributes, true);
                }
                shadows.push(shadow);
            }
        }
        return shadows;
    },

    
    drawSeries: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            surface = chart.surface,
            animate = chart.animate,
            stacked = me.stacked,
            column = me.column,
            chartAxes = chart.axes,
            boundAxes = me.getAxesForXAndYFields(),
            boundXAxis = boundAxes.xAxis,
            boundYAxis = boundAxes.yAxis,
            enableShadows = chart.shadow,
            shadowGroups = me.shadowGroups,
            shadowGroupsLn = shadowGroups.length,
            group = me.group,
            seriesStyle = me.seriesStyle,
            items, ln, i, j, baseAttrs, sprite, rendererAttributes, shadowIndex, shadowGroup,
            bounds, endSeriesStyle, barAttr, attrs, anim;

        if (!store || !store.getCount() || me.seriesIsHidden) {
            me.hide();
            me.items = [];
            return;
        }

        
        endSeriesStyle = Ext.apply({}, this.style, seriesStyle);
        delete endSeriesStyle.fill;
        delete endSeriesStyle.x;
        delete endSeriesStyle.y;
        delete endSeriesStyle.width;
        delete endSeriesStyle.height;
        
        me.unHighlightItem();
        me.cleanHighlights();
        
        me.boundColumn = (boundXAxis && Ext.Array.contains(me.axis,boundXAxis) 
                            && chartAxes.get(boundXAxis) 
                            && chartAxes.get(boundXAxis).isNumericAxis);

        me.getPaths();
        bounds = me.bounds;
        items = me.items;

        baseAttrs = column ? {
            y: bounds.zero,
            height: 0
        } : {
            x: bounds.zero,
            width: 0
        };
        ln = items.length;

        
        for (i = 0; i < ln; i++) {
            sprite = group.getAt(i);
            barAttr = items[i].attr;

            if (enableShadows) {
                items[i].shadows = me.renderShadows(i, barAttr, baseAttrs, bounds);
            }

            
            if (!sprite) {
                attrs = Ext.apply({}, baseAttrs, barAttr);
                attrs = Ext.apply(attrs, endSeriesStyle || {});
                sprite = surface.add(Ext.apply({}, {
                    type: 'rect',
                    group: group
                }, attrs));
            }
            if (animate) {
                rendererAttributes = me.renderer(sprite, store.getAt(i), barAttr, i, store);
                sprite._to = rendererAttributes;
                anim = me.onAnimate(sprite, {
                    zero: bounds.zero + bounds.bbox.width,  
                    to: Ext.apply(rendererAttributes, endSeriesStyle) 
                });
                if (enableShadows && stacked && (i % bounds.barsLen === 0)) {
                    j = i / bounds.barsLen;
                    for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) {
                        anim.on('afteranimate', function() {
                            this.show(true);
                        }, shadowGroups[shadowIndex].getAt(j));
                    }
                }
            }
            else {
                rendererAttributes = me.renderer(sprite, store.getAt(i), Ext.apply(barAttr, { hidden: false }), i, store);
                sprite.setAttributes(Ext.apply(rendererAttributes, endSeriesStyle), true);
            }
            items[i].sprite = sprite;
        }

        
        ln = group.getCount();
        for (j = i; j < ln; j++) {
            group.getAt(j).hide(true);
        }
        
        if (me.stacked) {
            
            i = store.getCount();    
        }
        
        
        if (enableShadows) {
            for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) {
                shadowGroup = shadowGroups[shadowIndex];
                ln = shadowGroup.getCount();
                for (j = i; j < ln; j++) {
                    shadowGroup.getAt(j).hide(true);
                }
            }
        }
        me.renderLabels();
    },

    
    onCreateLabel: function(storeItem, item, i, display) {
        var me = this,
            surface = me.chart.surface,
            group = me.labelsGroup,
            config = me.label,
            endLabelStyle = Ext.apply({}, config, me.seriesLabelStyle || {}),
            sprite;

        return surface.add(Ext.apply({
            type: 'text',
            group: group
        }, endLabelStyle || {}));
    },

    
    onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {
        
        
        var me = this,
            opt = me.bounds,
            groupBarWidth = opt.groupBarWidth,
            column = me.column,
            chart = me.chart,
            chartBBox = chart.chartBBox,
            resizing = chart.resizing,
            xValue = item.value[0],
            yValue = item.value[1],
            attr = item.attr,
            config = me.label,
            stacked = me.stacked,
            stackedDisplay = config.stackedDisplay,
            rotate = (config.orientation == 'vertical'),
            field = [].concat(config.field),
            format = config.renderer,
            text, size, width, height,
            zero = opt.zero,
            insideStart = 'insideStart',
            insideEnd = 'insideEnd',
            outside = 'outside',
            over = 'over',
            under = 'under',
            labelMarginX = 4,   
            labelMarginY = 2,
            signed = opt.signed,
            reverse = me.reverse,
            x, y, finalAttr;

        if (display == insideStart || display == insideEnd || display == outside) {
            if (stacked && (display == outside)) {
                
                
                label.hide(true);
                return;
            }
            label.setAttributes({
                
                
                style: undefined
            });
            text = (Ext.isNumber(index) ? format(storeItem.get(field[index]), label, storeItem, item, i, display, animate, index) : '');
            label.setAttributes({
                
                text: text
            });
            size = me.getLabelSize(text, label.attr.style);
            width = size.width;
            height = size.height;
            if (column) {
                
                
            
                
                
                if (!width || !height || (stacked && (attr.height < height))) {
                    label.hide(true);
                    return;
                }
                
                
                x = attr.x + (rotate ? groupBarWidth/2 : (groupBarWidth - width)/2);
                
                
                if (display == outside) {
                    var free = (yValue >= 0 ? (attr.y - chartBBox.y) : (chartBBox.y + chartBBox.height - attr.y - attr.height));
                    if (free < height + labelMarginY) {
                        display = insideEnd;
                    }
                }
    
                
                
                if (!stacked && (display != outside)) {
                    if (height + labelMarginY > attr.height) {
                        display = outside;
                    }
                }
    
                
                
                if (!y) {
                    y = attr.y;
                    if (yValue >= 0) {
                        switch (display) {
                            case insideStart: y += attr.height + (rotate ? -labelMarginY : -height/2);  break;
                            case insideEnd:   y += (rotate ? height + labelMarginX : height/2);         break;
                            case outside:     y += (rotate ? -labelMarginY : -height/2);                break;
                        }
                    } else {
                        switch (display) {
                            case insideStart: y += (rotate ? height + labelMarginY : height/2);                             break;
                            case insideEnd:   y += (rotate ? attr.height - labelMarginY : attr.height - height/2);          break;
                            case outside:     y += (rotate ? attr.height + height + labelMarginY : attr.height + height/2); break;
                        }
                    }
                }
            }
            else {
                
                
    
                
                if (!width || !height || (stacked && !attr.width)) {
                    label.hide(true);
                    return;
                }
    
                
                y = attr.y + (rotate ? (groupBarWidth + height)/2 : groupBarWidth/2);
    
                
                if (display == outside) {
                    var free = (yValue >= 0 ? (chartBBox.x + chartBBox.width - attr.x - attr.width) :  (attr.x - chartBBox.x));
                    if (free < width + labelMarginX) {
                        display = insideEnd;
                    }
                }
    
                
                
                
                if ((display != outside) && !rotate) {
                    
                    if (width + labelMarginX * 2 >= attr.width) {
                        if (stacked) {
                            if (height > attr.width) {
                                label.hide(true);
                                return; 
                            }
                            x = attr.x + attr.width/2;
                            rotate = true;
                        } else {
                            display = outside;
                        }
                    }
                }
    
                
                
                if (!x) {
                    x = attr.x;
                    if (yValue >= 0) {
                        switch (display) {
                            case insideStart:
                                if (reverse) {
                                    x += attr.width + (rotate ? -width/2 : -width - labelMarginX);
                                } else { 
                                    x += (rotate ? width/2 : labelMarginX);
                                }
                                break;
                            case insideEnd:   
                                if (reverse) {
                                    x -= rotate ? -width/2 : -width - labelMarginX;
                                } else {
                                    x += attr.width + (rotate ? -width/2 : -width - labelMarginX);
                                }    
                                break;
                            case outside:     
                                if (reverse) {
                                    x -= width + (rotate ? width/2 : labelMarginX);
                                } else {
                                    x += attr.width + (rotate ? width/2 : labelMarginX);
                                }         
                                break;
                        }
                    } else {
                        switch (display) {
                            case insideStart: 
                                if (reverse) {
                                    x -= rotate ? -width/2 : -width - labelMarginX;
                                } else {
                                    x += attr.width + (rotate ? -width/2 : -width - labelMarginX);
                                }    
                                break;
                            case insideEnd:
                                if (reverse) {
                                    x += attr.width + (rotate ? -width/2 : -width - labelMarginX);
                                } else {   
                                    x += (rotate ? width/2 : labelMarginX);
                                }                          
                                break;
                            case outside:
                                if (reverse) {
                                    x -= width + (rotate ? width/2 : labelMarginX);
                                } else {     
                                    x += (rotate ? -width/2 : -width - labelMarginX);
                                }                 
                                break;
                        }
                    }
                }
            }
        } else if (display == over || display == under) {
            if (stacked && stackedDisplay) {
                
                
    
                text = label.attr.text;
                label.setAttributes({
                    
                    
                    style: Ext.applyIf((label.attr && label.attr.style) || {},
                        {
                            'font-weight':'bold',
                            'font-size':'14px'
                        }
                    )
                });

                size = me.getLabelSize(text, label.attr.style);
                width = size.width;
                height = size.height;
    
                switch (display) {
                    case over:
                        if (column) {
                            x = attr.x + (rotate ? groupBarWidth/2 : (groupBarWidth - width)/2);
                            y = zero - (item.totalDim - item.totalNegDim) - height/2 - labelMarginY;
                        } else {
                            x = zero + (item.totalDim - item.totalNegDim) + labelMarginX;
                            y = attr.y + (rotate ? (groupBarWidth + height)/2 : groupBarWidth/2);
                        }
                        break;
                    case under:
                        if (column) {
                            x = attr.x + (rotate ? groupBarWidth/2 : (groupBarWidth - width)/2);
                            y = zero + item.totalNegDim + height/2;
                        } else {
                            x = zero - item.totalNegDim - width - labelMarginX;
                            y = attr.y + (rotate ? (groupBarWidth + height)/2 : groupBarWidth/2);
                        }
                        break;
                }
            }
        }
        
        if (x == undefined || y == undefined) {
            
            label.hide(true);
            return;
        }

        label.isOutside = (display == outside);
        label.setAttributes({
            text: text
        });

        
        finalAttr = {
            x: x,
            y: y
        };
        
        
        
        finalAttr.rotate = rotate ? {
            x: x,
            y: y,
            degrees: 270
        } : me.defaultRotate;
        
        
        if (animate && resizing) {
            if (column) {
                x = attr.x + attr.width / 2;
                y = zero;
            } else {
                x = zero;
                y = attr.y + attr.height / 2;
            }
            label.setAttributes({
                x: x,
                y: y
            }, true);
            if (rotate) {
                label.setAttributes({
                    rotate: {
                        x: x,
                        y: y,
                        degrees: 270
                    }
                }, true);
            }
        }
        
        
        if (animate) {
            me.onAnimate(label, {
                zero: item.point[0], 
                to: finalAttr 
            });
        }
        else {
            label.setAttributes(Ext.apply(finalAttr, {
                hidden: false
            }), true);
        }
    },

    
    getLabelSize: function(value, labelStyle) {
        var tester = this.testerLabel,
            config = this.label,
            endLabelStyle = Ext.apply({}, config, labelStyle, this.seriesLabelStyle || {}),
            rotated = config.orientation === 'vertical',
            bbox, w, h,
            undef;
        if (!tester) {
            tester = this.testerLabel = this.chart.surface.add(Ext.apply({
                type: 'text',
                opacity: 0
            }, endLabelStyle));
        }
        tester.setAttributes({
            style: labelStyle,
            text: value
        }, true);

        
        bbox = tester.getBBox();
        w = bbox.width;
        h = bbox.height;
        return {
            width: rotated ? h : w,
            height: rotated ? w : h
        };
    },

    
    onAnimate: function(sprite, attr) {
        var me = this,
            to = attr.to;
            
        sprite.show();
        
        if (me.reverse && !me.column) {
            attr.from = {
                x: (me.stacked || sprite.type == 'text') ? attr.zero : to.x + to.width,
                width: 0
            };
            
            
            
            if (me.inHighlight) {
                delete attr.from.x;
            }
        }
        
        return this.callParent(arguments);
    },

    isItemInPoint: function(x, y, item) {
        var bbox = item.sprite.getBBox();
        return bbox.x <= x && bbox.y <= y
            && (bbox.x + bbox.width) >= x
            && (bbox.y + bbox.height) >= y;
    },

    
    hideAll: function(index) {
        var axes      = this.chart.axes,
            axesItems = axes.items,
            ln        = axesItems.length,
            i         = 0;

        index = (isNaN(this._index) ? index : this._index) || 0;

        if (!this.__excludes) {
            this.__excludes = [];
        }

        this.__excludes[index] = true;
        this.drawSeries();

        for (i; i < ln; i++) {
            axesItems[i].drawAxis();
        }    
    },

    
    showAll: function(index) {
        var axes = this.chart.axes,
            axesItems = axes.items,
            ln        = axesItems.length,
            i         = 0;

        index = (isNaN(this._index) ? index : this._index) || 0;

        if (!this.__excludes) {
            this.__excludes = [];
        }

        this.__excludes[index] = false;
        this.drawSeries();

        for (i; i < ln; i++) {
            axesItems[i].drawAxis();
        }    
    },

    
    getLegendColor: function(index) {
        var me = this,
            colors = me.colorArrayStyle,
            colorLength = colors && colors.length;

        if (me.style && me.style.fill) {
            return me.style.fill;
        } else {
            return (colors ? colors[(me.themeIdx + index) % colorLength] : '#000');
        }
    },

    highlightItem: function(item) {
        this.callParent(arguments);
        this.inHighlight = true;
        this.renderLabels();
        delete this.inHighlight;
    },

    unHighlightItem: function() {
        this.callParent(arguments);
        this.inHighlight = true;
        this.renderLabels();
        delete this.inHighlight;
    },

    cleanHighlights: function() {
        this.callParent(arguments);
        this.inHighlight = true;
        this.renderLabels();
        delete this.inHighlight;
    }
});


Ext.define('Ext.chart.series.Column', {

    

    alternateClassName: ['Ext.chart.ColumnSeries', 'Ext.chart.ColumnChart', 'Ext.chart.StackedColumnChart'],

    extend:  Ext.chart.series.Bar ,

    

    type: 'column',
    alias: 'series.column',

    column: true,

    
    boundColumn: false,

    

    
    xPadding: 10,

    
    yPadding: 0
});


Ext.define('Ext.chart.series.Gauge', {

    

    extend:  Ext.chart.series.Series ,

    

    type: "gauge",
    alias: 'series.gauge',

    rad: Math.PI / 180,

    
    highlightDuration: 150,

    
    angleField: false,

    
    needle: false,
    
    
    donut: false,

    
    showInLegend: false,

    
    style: {},
    
    constructor: function(config) {
        this.callParent(arguments);
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            store = chart.store,
            shadow = chart.shadow, i, l, cfg;
        Ext.apply(me, config, {
            shadowAttributes: [{
                "stroke-width": 6,
                "stroke-opacity": 1,
                stroke: 'rgb(200, 200, 200)',
                translate: {
                    x: 1.2,
                    y: 2
                }
            },
            {
                "stroke-width": 4,
                "stroke-opacity": 1,
                stroke: 'rgb(150, 150, 150)',
                translate: {
                    x: 0.9,
                    y: 1.5
                }
            },
            {
                "stroke-width": 2,
                "stroke-opacity": 1,
                stroke: 'rgb(100, 100, 100)',
                translate: {
                    x: 0.6,
                    y: 1
                }
            }]
        });
        me.group = surface.getGroup(me.seriesId);
        if (shadow) {
            for (i = 0, l = me.shadowAttributes.length; i < l; i++) {
                me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
            }
        }
        surface.customAttributes.segment = function(opt) {
            return me.getSegment(opt);
        };
    },
    
    
    initialize: function() {
        var me = this,
            store = me.chart.getChartStore(),
            data = store.data.items,
            label = me.label,
            ln = data.length;
          
        me.yField = [];
        if (label && label.field && ln > 0) {
            me.yField.push(data[0].get(label.field));
        }
    },

    
    getSegment: function(opt) {
        var me = this,
            rad = me.rad,
            cos = Math.cos,
            sin = Math.sin,
            abs = Math.abs,
            x = me.centerX,
            y = me.centerY,
            x1 = 0, x2 = 0, x3 = 0, x4 = 0,
            y1 = 0, y2 = 0, y3 = 0, y4 = 0,
            delta = 1e-2,
            r = opt.endRho - opt.startRho,
            startAngle = opt.startAngle,
            endAngle = opt.endAngle,
            midAngle = (startAngle + endAngle) / 2 * rad,
            margin = opt.margin || 0,
            flag = abs(endAngle - startAngle) > 180,
            a1 = Math.min(startAngle, endAngle) * rad,
            a2 = Math.max(startAngle, endAngle) * rad,
            singleSlice = false;

        x += margin * cos(midAngle);
        y += margin * sin(midAngle);

        x1 = x + opt.startRho * cos(a1);
        y1 = y + opt.startRho * sin(a1);

        x2 = x + opt.endRho * cos(a1);
        y2 = y + opt.endRho * sin(a1);

        x3 = x + opt.startRho * cos(a2);
        y3 = y + opt.startRho * sin(a2);

        x4 = x + opt.endRho * cos(a2);
        y4 = y + opt.endRho * sin(a2);

        if (abs(x1 - x3) <= delta && abs(y1 - y3) <= delta) {
            singleSlice = true;
        }
        
        if (singleSlice) {
            return {
                path: [
                ["M", x1, y1],
                ["L", x2, y2],
                ["A", opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
                ["Z"]]
            };
        } else {
            return {
                path: [
                ["M", x1, y1],
                ["L", x2, y2],
                ["A", opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
                ["L", x3, y3],
                ["A", opt.startRho, opt.startRho, 0, +flag, 0, x1, y1],
                ["Z"]]
            };
        }
    },

    
    calcMiddle: function(item) {
        var me = this,
            rad = me.rad,
            slice = item.slice,
            x = me.centerX,
            y = me.centerY,
            startAngle = slice.startAngle,
            endAngle = slice.endAngle,
            radius = Math.max(('rho' in slice) ? slice.rho: me.radius, me.label.minMargin),
            donut = +me.donut,
            a1 = Math.min(startAngle, endAngle) * rad,
            a2 = Math.max(startAngle, endAngle) * rad,
            midAngle = -(a1 + (a2 - a1) / 2),
            xm = x + (item.endRho + item.startRho) / 2 * Math.cos(midAngle),
            ym = y - (item.endRho + item.startRho) / 2 * Math.sin(midAngle);

        item.middle = {
            x: xm,
            y: ym
        };
    },

    
    drawSeries: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            group = me.group,
            animate = me.chart.animate,
            axis = me.chart.axes.get(0),
            minimum = axis && axis.minimum || me.minimum || 0,
            maximum = axis && axis.maximum || me.maximum || 0,
            field = me.angleField || me.field || me.xField,
            surface = chart.surface,
            chartBBox = chart.chartBBox,
            rad = me.rad,
            donut = +me.donut,
            values = {},
            items = [],
            seriesStyle = me.seriesStyle,
            seriesLabelStyle = me.seriesLabelStyle,
            colorArrayStyle = me.colorArrayStyle,
            colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0,
            cos = Math.cos,
            sin = Math.sin,
            defaultStart = -180,
            reverse = me.reverse,
            rendererAttributes, centerX, centerY, slice, slices, sprite, value,
            item, ln, record, i, j, startAngle, endAngle, middleAngle, sliceLength, path,
            p, spriteOptions, bbox, splitAngle, sliceA, sliceB;
        
        Ext.apply(seriesStyle, me.style || {});

        me.setBBox();
        bbox = me.bbox;

        
        if (me.colorSet) {
            colorArrayStyle = me.colorSet;
            colorArrayLength = colorArrayStyle.length;
        }
        
        
        if (!store || !store.getCount() || me.seriesIsHidden) {
            me.hide();
            me.items = [];
            return;
        }
        
        centerX = me.centerX = chartBBox.x + (chartBBox.width / 2);
        centerY = me.centerY = chartBBox.y + chartBBox.height;
        me.radius = Math.min(centerX - chartBBox.x, centerY - chartBBox.y);
        me.slices = slices = [];
        me.items = items = [];
        
        if (!me.value) {
            record = store.getAt(0);
            me.value = record.get(field);
        }
        
        value = reverse ? maximum - me.value : me.value;
        if (me.needle) {
            sliceA = {
                series: me,
                value: value,
                startAngle: defaultStart,
                endAngle: 0,
                rho: me.radius
            };
            splitAngle = defaultStart * (1 - (value - minimum) / (maximum - minimum));
            slices.push(sliceA);
        } else {
            splitAngle = defaultStart * (1 - (value - minimum) / (maximum - minimum));
            sliceA = {
                series: me,
                value: value,
                startAngle: defaultStart,
                endAngle: splitAngle,
                rho: me.radius
            };
            sliceB = {
                series: me,
                value: maximum - value,
                startAngle: splitAngle,
                endAngle: 0,
                rho: me.radius
            };
            
            if (reverse) {
                slices.push(sliceB, sliceA);
            } else {
                slices.push(sliceA, sliceB);
            }
        }
        
        
        for (i = 0, ln = slices.length; i < ln; i++) {
            slice = slices[i];
            sprite = group.getAt(i);
            
            rendererAttributes = Ext.apply({
                segment: {
                    startAngle: slice.startAngle,
                    endAngle: slice.endAngle,
                    margin: 0,
                    rho: slice.rho,
                    startRho: slice.rho * +donut / 100,
                    endRho: slice.rho
                } 
            }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[i % colorArrayLength] } || {}));

            item = Ext.apply({},
            rendererAttributes.segment, {
                slice: slice,
                series: me,
                storeItem: record,
                index: i
            });
            items[i] = item;
            
            if (!sprite) {
                spriteOptions = Ext.apply({
                    type: "path",
                    group: group
                }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[i % colorArrayLength] } || {}));
                sprite = surface.add(Ext.apply(spriteOptions, rendererAttributes));
            }
            slice.sprite = slice.sprite || [];
            item.sprite = sprite;
            slice.sprite.push(sprite);
            if (animate) {
                rendererAttributes = me.renderer(sprite, record, rendererAttributes, i, store);
                sprite._to = rendererAttributes;
                me.onAnimate(sprite, {
                    to: rendererAttributes
                });
            } else {
                rendererAttributes = me.renderer(sprite, record, Ext.apply(rendererAttributes, {
                    hidden: false
                }), i, store);
                sprite.setAttributes(rendererAttributes, true);
            }
        }
        
        if (me.needle) {
            splitAngle = splitAngle * Math.PI / 180;
            
            if (!me.needleSprite) {
                me.needleSprite = me.chart.surface.add({
                    type: 'path',
                    path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
                                centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
                           'L', centerX + me.radius * cos(splitAngle),
                                centerY + -Math.abs(me.radius * sin(splitAngle))],
                    'stroke-width': 4,
                    'stroke': '#222'
                });
            } else {
                if (animate) {
                    me.onAnimate(me.needleSprite, {
                        to: {
                        path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
                                    centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
                               'L', centerX + me.radius * cos(splitAngle),
                                    centerY + -Math.abs(me.radius * sin(splitAngle))]
                        }
                    });
                } else {
                    me.needleSprite.setAttributes({
                        type: 'path',
                        path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
                                    centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
                               'L', centerX + me.radius * cos(splitAngle),
                                    centerY + -Math.abs(me.radius * sin(splitAngle))]
                    });
                }
            }
            me.needleSprite.setAttributes({
                hidden: false    
            }, true);
        }
        
        delete me.value;
    },
    
    
    setValue: function (value) {
        this.value = value;
        this.drawSeries();
    },

    
    onCreateLabel: function(storeItem, item, i, display) {},

    
    onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {},

    
    onPlaceCallout: function() {},

    
    onAnimate: function(sprite, attr) {
        sprite.show();
        return this.callParent(arguments);
    },

    isItemInPoint: function(x, y, item, i) {
        var me = this,
            cx = me.centerX,
            cy = me.centerY,
            abs = Math.abs,
            dx = abs(x - cx),
            dy = abs(y - cy),
            startAngle = item.startAngle,
            endAngle = item.endAngle,
            rho = Math.sqrt(dx * dx + dy * dy),
            angle = Math.atan2(y - cy, x - cx) / me.rad;

        
        return (i === 0) && (angle >= startAngle && angle < endAngle && 
                             rho >= item.startRho && rho <= item.endRho);
    },
    
    
    getLegendColor: function(index) {
        var colors = this.colorSet || this.colorArrayStyle;
        return colors[index % colors.length];
    }
});



Ext.define('Ext.chart.series.Line', {

    

    extend:  Ext.chart.series.Cartesian ,

    alternateClassName: ['Ext.chart.LineSeries', 'Ext.chart.LineChart'],

                                                                                         

    

    type: 'line',

    alias: 'series.line',

    
    selectionTolerance: 20,

    
    showMarkers: true,

    
    markerConfig: {},

    
    style: {},

    
    smooth: false,

    
    defaultSmoothness: 3,

    
    fill: false,

    constructor: function(config) {
        this.callParent(arguments);
        var me = this,
            surface = me.chart.surface,
            shadow = me.chart.shadow,
            i, l;
        config.highlightCfg = Ext.Object.merge({ 'stroke-width': 3 }, config.highlightCfg);
        Ext.apply(me, config, {
            shadowAttributes: [{
                "stroke-width": 6,
                "stroke-opacity": 0.05,
                stroke: 'rgb(0, 0, 0)',
                translate: {
                    x: 1,
                    y: 1
                }
            }, {
                "stroke-width": 4,
                "stroke-opacity": 0.1,
                stroke: 'rgb(0, 0, 0)',
                translate: {
                    x: 1,
                    y: 1
                }
            }, {
                "stroke-width": 2,
                "stroke-opacity": 0.15,
                stroke: 'rgb(0, 0, 0)',
                translate: {
                    x: 1,
                    y: 1
                }
            }]
        });
        me.group = surface.getGroup(me.seriesId);
        if (me.showMarkers) {
            me.markerGroup = surface.getGroup(me.seriesId + '-markers');
        }
        if (shadow) {
            for (i = 0, l = me.shadowAttributes.length; i < l; i++) {
                me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
            }
        }
    },

    
    shrink: function(xValues, yValues, size) {
        
        var len = xValues.length,
            ratio = Math.floor(len / size),
            i = 1,
            xSum = 0,
            ySum = 0,
            xRes = [+xValues[0]],
            yRes = [+yValues[0]];

        for (; i < len; ++i) {
            xSum += +xValues[i] || 0;
            ySum += +yValues[i] || 0;
            if (i % ratio == 0) {
                xRes.push(xSum/ratio);
                yRes.push(ySum/ratio);
                xSum = 0;
                ySum = 0;
            }
        }
        return {
            x: xRes,
            y: yRes
        };
    },

    
    drawSeries: function() {
        var me = this,
            chart = me.chart,
            chartAxes = chart.axes,
            store = chart.getChartStore(),
            data = store.data.items,
            record,
            storeCount = store.getCount(),
            surface = me.chart.surface,
            bbox = {},
            group = me.group,
            showMarkers = me.showMarkers,
            markerGroup = me.markerGroup,
            enableShadows = chart.shadow,
            shadowGroups = me.shadowGroups,
            shadowAttributes = me.shadowAttributes,
            smooth = me.smooth,
            lnsh = shadowGroups.length,
            dummyPath = ["M"],
            path = ["M"],
            renderPath = ["M"],
            smoothPath = ["M"],
            markerIndex = chart.markerIndex,
            axes = [].concat(me.axis),
            shadowBarAttr,
            xValues = [],
            yValues = [],
            onbreak = false,
            reverse = me.reverse,
            storeIndices = [],
            markerStyle = Ext.apply({}, me.markerStyle),
            seriesStyle = me.seriesStyle,
            colorArrayStyle = me.colorArrayStyle,
            colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0,
            isNumber = Ext.isNumber,
            seriesIdx = me.seriesIdx, 
            boundAxes = me.getAxesForXAndYFields(),
            boundXAxis = boundAxes.xAxis,
            boundYAxis = boundAxes.yAxis,
            xAxis = chartAxes && chartAxes.get(boundXAxis),
            yAxis = chartAxes && chartAxes.get(boundYAxis),
            xAxisType = boundXAxis ? xAxis && xAxis.type : '',
            yAxisType = boundYAxis ? yAxis && yAxis.type : '',
            shadows, shadow, shindex, fromPath, fill, fillPath, rendererAttributes,
            x, y, prevX, prevY, firstX, firstY, markerCount, i, j, ln, axis, ends, marker, markerAux, item, xValue,
            yValue, coords, xScale, yScale, minX, maxX, minY, maxY, line, animation, endMarkerStyle,
            endLineStyle, type, count, opacity, lineOpacity, fillOpacity, fillDefaultValue;

        if (me.fireEvent('beforedraw', me) === false) {
            return;
        }

        
        if (!storeCount || me.seriesIsHidden) {
            me.hide();
            me.items = [];
            if (me.line) {
                me.line.hide(true);
                if (me.line.shadows) {
                    shadows = me.line.shadows;
                    for (j = 0, lnsh = shadows.length; j < lnsh; j++) {
                        shadow = shadows[j];
                        shadow.hide(true);
                    }
                }
                if (me.fillPath) {
                    me.fillPath.hide(true);
                }
            }
            me.line = null;
            me.fillPath = null;
            return;
        }

        
        endMarkerStyle = Ext.apply(markerStyle || {}, me.markerConfig, {
            fill: me.seriesStyle.fill || colorArrayStyle[me.themeIdx % colorArrayStyle.length]
        });
        type = endMarkerStyle.type;
        delete endMarkerStyle.type;
        endLineStyle = seriesStyle;
        
        
        if (!endLineStyle['stroke-width']) {
            endLineStyle['stroke-width'] = 0.5;
        }
        
        
        opacity = 'opacity' in endLineStyle ? endLineStyle.opacity : 1;
        fillDefaultValue = 'opacity' in endLineStyle ? endLineStyle.opacity : 0.3;
        lineOpacity = 'lineOpacity' in endLineStyle ? endLineStyle.lineOpacity : opacity;
        fillOpacity = 'fillOpacity' in endLineStyle ? endLineStyle.fillOpacity : fillDefaultValue;

        
        
        if (markerIndex && markerGroup && markerGroup.getCount()) {
            for (i = 0; i < markerIndex; i++) {
                marker = markerGroup.getAt(i);
                markerGroup.remove(marker);
                markerGroup.add(marker);
                markerAux = markerGroup.getAt(markerGroup.getCount() - 2);
                marker.setAttributes({
                    x: 0,
                    y: 0,
                    translate: {
                        x: markerAux.attr.translation.x,
                        y: markerAux.attr.translation.y
                    }
                }, true);
            }
        }

        me.unHighlightItem();
        me.cleanHighlights();

        me.setBBox();
        bbox = me.bbox;
        me.clipRect = [bbox.x, bbox.y, bbox.width, bbox.height];

        if (xAxis) {
            ends = xAxis.applyData();
            minX = ends.from;
            maxX = ends.to;
        }

        if (yAxis) {
            ends = yAxis.applyData();
            minY = ends.from;
            maxY = ends.to;
        }

        
        if (me.xField && !Ext.isNumber(minX)) {
            axis = me.getMinMaxXValues();
            minX = axis[0];
            maxX = axis[1];
        }

        if (me.yField && !Ext.isNumber(minY)) {
            axis = me.getMinMaxYValues();
            minY = axis[0];
            maxY = axis[1];
        }
        
        if (isNaN(minX)) {
            minX = 0;
            xScale = bbox.width / ((storeCount - 1) || 1);
        }
        else {
            xScale = bbox.width / ((maxX - minX) || (storeCount -1) || 1);
        }

        if (isNaN(minY)) {
            minY = 0;
            yScale = bbox.height / ((storeCount - 1) || 1);
        }
        else {
            yScale = bbox.height / ((maxY - minY) || (storeCount - 1) || 1);
        }
        
        
        for (i = 0, ln = data.length; i < ln; i++) {
            record = data[i];
            xValue = record.get(me.xField);
            if (xAxisType === 'Time' && typeof xValue === "string") {
                xValue = Date.parse(xValue);
            }
            
            if (typeof xValue === 'string' || typeof xValue === 'object' && !Ext.isDate(xValue)
                
                || xAxisType === 'Category') {
                    xValue = i;
            }

            
            yValue = record.get(me.yField);
            
            if (yAxisType === 'Time' && typeof yValue === "string") {
                yValue = Date.parse(yValue);
            }
            
            
            if (typeof yValue === 'undefined' || (typeof yValue === 'string' && !yValue)) {
                continue;
            }
            
            
            if (typeof yValue === 'string' || typeof yValue === 'object' && !Ext.isDate(yValue)
                
                || yAxisType === 'Category') {
                yValue = i;
            }
            storeIndices.push(i);
            xValues.push(xValue);
            yValues.push(yValue);
        }

        ln = xValues.length;
        if (ln > bbox.width) {
            coords = me.shrink(xValues, yValues, bbox.width);
            xValues = coords.x;
            yValues = coords.y;
        }

        me.items = [];

        count = 0;
        ln = xValues.length;
        for (i = 0; i < ln; i++) {
            xValue = xValues[i];
            yValue = yValues[i];
            if (yValue === false) {
                if (path.length == 1) {
                    path = [];
                }
                onbreak = true;
                me.items.push(false);
                continue;
            } else {
                if (reverse) {
                    x = bbox.x + bbox.width - ((xValue - minX) * xScale);
                } else {
                    x = (bbox.x + (xValue - minX) * xScale);
                }
                x = Ext.Number.toFixed(x, 2);
                y = Ext.Number.toFixed((bbox.y + bbox.height) - (yValue - minY) * yScale, 2);
                if (onbreak) {
                    onbreak = false;
                    path.push('M');
                }
                path = path.concat([x, y]);
            }
            if ((typeof firstY == 'undefined') && (typeof y != 'undefined')) {
                firstY = y;
                firstX = x;
            }
            
            if (!me.line || chart.resizing) {
                dummyPath = dummyPath.concat([x, bbox.y + bbox.height / 2]);
            }

            
            if (chart.animate && chart.resizing && me.line) {
                me.line.setAttributes({
                    path: dummyPath,
                    opacity: lineOpacity
                }, true);
                if (me.fillPath) {
                    me.fillPath.setAttributes({
                        path: dummyPath,
                        opacity: fillOpacity
                    }, true);
                }
                if (me.line.shadows) {
                    shadows = me.line.shadows;
                    for (j = 0, lnsh = shadows.length; j < lnsh; j++) {
                        shadow = shadows[j];
                        shadow.setAttributes({
                            path: dummyPath
                        }, true);
                    }
                }
            }
            if (showMarkers) {
                marker = markerGroup.getAt(count++);
                if (!marker) {
                    marker = Ext.chart.Shape[type](surface, Ext.apply({
                        group: [group, markerGroup],
                        x: 0, y: 0,
                        translate: {
                            x: +(prevX || x),
                            y: prevY || (bbox.y + bbox.height / 2)
                        },
                        value: '"' + xValue + ', ' + yValue + '"',
                        zIndex: 4000
                    }, endMarkerStyle));
                    marker._to = {
                        translate: {
                            x: +x,
                            y: +y
                        }
                    };
                } else {
                    marker.setAttributes({
                        value: '"' + xValue + ', ' + yValue + '"',
                        x: 0, y: 0,
                        hidden: false
                    }, true);
                    marker._to = {
                        translate: {
                            x: +x, 
                            y: +y
                        }
                    };
                }
            }
            
            me.items.push({
                series: me,
                value: [xValue, yValue],
                point: [x, y],
                sprite: marker,
                storeItem: store.getAt(storeIndices[i])
            });
            prevX = x;
            prevY = y;
        }

        if (path.length <= 1) {
            
            return;
        }

        if (me.smooth) {
            smoothPath = Ext.draw.Draw.smooth(path, isNumber(smooth) ? smooth : me.defaultSmoothness);
        }

        renderPath = smooth ? smoothPath : path;

        
        if (chart.markerIndex && me.previousPath) {
            fromPath = me.previousPath;
            if (!smooth) {
                Ext.Array.erase(fromPath, 1, 2);
            }
        } else {
            fromPath = path;
        }

        
        if (!me.line) {
            me.line = surface.add(Ext.apply({
                type: 'path',
                group: group,
                path: dummyPath,
                stroke: endLineStyle.stroke || endLineStyle.fill
            }, endLineStyle || {}));
            me

            
            me.line.setAttributes({
                opacity: lineOpacity
            }, true);

            if (enableShadows) {
                me.line.setAttributes(Ext.apply({}, me.shadowOptions), true);
            }

            
            me.line.setAttributes({
                fill: 'none',
                zIndex: 3000
            });
            if (!endLineStyle.stroke && colorArrayLength) {
                me.line.setAttributes({
                    stroke: colorArrayStyle[me.themeIdx % colorArrayLength]
                }, true);
            }
            if (enableShadows) {
                
                shadows = me.line.shadows = [];
                for (shindex = 0; shindex < lnsh; shindex++) {
                    shadowBarAttr = shadowAttributes[shindex];
                    shadowBarAttr = Ext.apply({}, shadowBarAttr, { path: dummyPath });
                    shadow = surface.add(Ext.apply({}, {
                        type: 'path',
                        group: shadowGroups[shindex]
                    }, shadowBarAttr));
                    shadows.push(shadow);
                }
            }
        }
        if (me.fill) {
            fillPath = renderPath.concat([
                ["L", x, bbox.y + bbox.height],
                ["L", firstX, bbox.y + bbox.height],
                ["L", firstX, firstY]
            ]);
            if (!me.fillPath) {
                me.fillPath = surface.add({
                    group: group,
                    type: 'path',
                    fill: endLineStyle.fill || colorArrayStyle[me.themeIdx % colorArrayLength],
                    path: dummyPath
                });
            }
        }
        markerCount = showMarkers && markerGroup.getCount();
        if (chart.animate) {
            fill = me.fill;
            line = me.line;
            
            rendererAttributes = me.renderer(line, false, { path: renderPath }, i, store);
            Ext.apply(rendererAttributes, endLineStyle || {}, {
                stroke: endLineStyle.stroke || endLineStyle.fill
            });
            
            delete rendererAttributes.fill;
            line.show(true);
            if (chart.markerIndex && me.previousPath) {
                me.animation = animation = me.onAnimate(line, {
                    to: rendererAttributes,
                    from: {
                        path: fromPath
                    }
                });
            } else {
                me.animation = animation = me.onAnimate(line, {
                    to: rendererAttributes
                });
            }
            
            if (enableShadows) {
                shadows = line.shadows;
                for(j = 0; j < lnsh; j++) {
                    shadows[j].show(true);
                    if (chart.markerIndex && me.previousPath) {
                        me.onAnimate(shadows[j], {
                            to: { path: renderPath },
                            from: { path: fromPath }
                        });
                    } else {
                        me.onAnimate(shadows[j], {
                            to: { path: renderPath }
                        });
                    }
                }
            }
            
            if (fill) {
                me.fillPath.show(true);
                me.onAnimate(me.fillPath, {
                    to: Ext.apply({}, {
                        path: fillPath,
                        fill: endLineStyle.fill || colorArrayStyle[me.themeIdx % colorArrayLength],
                        'stroke-width': 0,
                        opacity: fillOpacity
                    }, endLineStyle || {})
                });
            }
            
            if (showMarkers) {
                count = 0;
                for(i = 0; i < ln; i++) {
                    if (me.items[i]) {
                        item = markerGroup.getAt(count++);
                        if (item) {
                            rendererAttributes = me.renderer(item, store.getAt(i), item._to, i, store);
                            me.onAnimate(item, {
                                to: Ext.applyIf(rendererAttributes, endMarkerStyle || {})
                            });
                            item.show(true);
                        }
                    }
                }
                for(; count < markerCount; count++) {
                    item = markerGroup.getAt(count);
                    item.hide(true);
                }




            }
        } else {
            rendererAttributes = me.renderer(me.line, false, { path: renderPath, hidden: false }, i, store);
            Ext.apply(rendererAttributes, endLineStyle || {}, {
                stroke: endLineStyle.stroke || endLineStyle.fill
            });
            
            delete rendererAttributes.fill;
            me.line.setAttributes(rendererAttributes, true);
            me.line.setAttributes({
                opacity: lineOpacity
            }, true);
            
            if (enableShadows) {
                shadows = me.line.shadows;
                for(j = 0; j < lnsh; j++) {
                    shadows[j].setAttributes({
                        path: renderPath,
                        hidden: false
                    }, true);
                }
            }
            if (me.fill) {
                me.fillPath.setAttributes({
                    path: fillPath,
                    hidden: false,
                    opacity: fillOpacity
                }, true);
            }
            if (showMarkers) {
                count = 0;
                for(i = 0; i < ln; i++) {
                    if (me.items[i]) {
                        item = markerGroup.getAt(count++);
                        if (item) {
                            rendererAttributes = me.renderer(item, store.getAt(i), item._to, i, store);
                            item.setAttributes(Ext.apply(endMarkerStyle || {}, rendererAttributes || {}), true);
                            if (!item.attr.hidden) {
                                item.show(true);
                            }
                        }
                    }
                }
                for(; count < markerCount; count++) {
                    item = markerGroup.getAt(count);
                    item.hide(true);
                }
            }
        }

        if (chart.markerIndex) {
            if (me.smooth) {
                Ext.Array.erase(path, 1, 2);
            } else {
                Ext.Array.splice(path, 1, 0, path[1], path[2]);
            }
            me.previousPath = path;
        }
        me.renderLabels();
        me.renderCallouts();

        me.fireEvent('draw', me);
    },

    
    onCreateLabel: function(storeItem, item, i, display) {
        var me = this,
            group = me.labelsGroup,
            config = me.label,
            bbox = me.bbox,
            endLabelStyle = Ext.apply({}, config, me.seriesLabelStyle || {});

        return me.chart.surface.add(Ext.apply({
            'type': 'text',
            'text-anchor': 'middle',
            'group': group,
            'x': Number(item.point[0]),
            'y': bbox.y + bbox.height / 2
        }, endLabelStyle || {}));
    },

    
    onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {
        var me = this,
            chart = me.chart,
            resizing = chart.resizing,
            config = me.label,
            format = config.renderer,
            field = config.field,
            bbox = me.bbox,
            x = Number(item.point[0]),
            y = Number(item.point[1]),
            radius = item.sprite.attr.radius,
            labelBox, markerBox, width, height, xOffset, yOffset;

        label.setAttributes({
            text: format(storeItem.get(field), label, storeItem, item, i, display, animate, index),
            hidden: true
        }, true);

        
        markerBox = item.sprite.getBBox();
        markerBox.width = markerBox.width || (radius * 2);
        markerBox.height = markerBox.height || (radius * 2);

        labelBox = label.getBBox();
        width = labelBox.width/2;
        height = labelBox.height/2;

        if (display == 'rotate') {
            
            xOffset = markerBox.width/2 + width + height/2;
            if (x + xOffset + width > bbox.x + bbox.width) {
                x -= xOffset;
            } else {
                x += xOffset;
            }
            label.setAttributes({
                'rotation': {
                    x: x,
                    y: y,
                    degrees: -45
                }
            }, true);
        } else if (display == 'under' || display == 'over') {
            label.setAttributes({
                'rotation': {
                    degrees: 0
                }
            }, true);

            
            if (x < bbox.x + width) {
                x = bbox.x + width;
            } else if (x + width > bbox.x + bbox.width) {
                x = bbox.x + bbox.width - width;
            }

            yOffset = markerBox.height/2 + height;
            y = y + (display == 'over' ? -yOffset : yOffset);
            if (y < bbox.y + height) {
                y += 2 * yOffset;
            } else if (y + height > bbox.y + bbox.height) {
                y -= 2 * yOffset;
            }
        }

        if (me.chart.animate && !me.chart.resizing) {
            label.show(true);
            me.onAnimate(label, {
                to: {
                    x: x,
                    y: y
                }
            });
        } else {
            label.setAttributes({
                x: x,
                y: y
            }, true);
            if (resizing && me.animation) {
                me.animation.on('afteranimate', function() {
                    label.show(true);
                });
            } else {
                label.show(true);
            }
        }
    },

    
    highlightItem: function() {
        var me = this,
            line = me.line;
                
        me.callParent(arguments);
        if (line && !me.highlighted) {
            if (!('__strokeWidth' in line)) {
                line.__strokeWidth = parseFloat(line.attr['stroke-width']) || 0;
            }
            if (line.__anim) {
                line.__anim.paused = true;
            }
            
            line.__anim = new Ext.fx.Anim({
                target: line,
                to: {
                    'stroke-width': line.__strokeWidth + 3
                }
            });
            me.highlighted = true;
        }
    },

    
    unHighlightItem: function() {
        var me = this,
            line = me.line,
            width;
            
        me.callParent(arguments);
        if (line && me.highlighted) {
            width = line.__strokeWidth || parseFloat(line.attr['stroke-width']) || 0;
            line.__anim = new Ext.fx.Anim({
                target: line,
                to: {
                    'stroke-width': width
                }
            });
            me.highlighted = false;
        }
    },

    
    onPlaceCallout : function(callout, storeItem, item, i, display, animate, index) {
        if (!display) {
            return;
        }

        var me = this,
            chart = me.chart,
            surface = chart.surface,
            resizing = chart.resizing,
            config = me.callouts,
            items = me.items,
            prev = i == 0? false : items[i -1].point,
            next = (i == items.length -1)? false : items[i +1].point,
            cur = [+item.point[0], +item.point[1]],
            dir, norm, normal, a, aprev, anext,
            offsetFromViz = config.offsetFromViz || 30,
            offsetToSide = config.offsetToSide || 10,
            offsetBox = config.offsetBox || 3,
            boxx, boxy, boxw, boxh,
            p, clipRect = me.clipRect,
            bbox = {
                width: config.styles.width || 10,
                height: config.styles.height || 10
            },
            x, y;

        
        if (!prev) {
            prev = cur;
        }
        if (!next) {
            next = cur;
        }
        a = (next[1] - prev[1]) / (next[0] - prev[0]);
        aprev = (cur[1] - prev[1]) / (cur[0] - prev[0]);
        anext = (next[1] - cur[1]) / (next[0] - cur[0]);

        norm = Math.sqrt(1 + a * a);
        dir = [1 / norm, a / norm];
        normal = [-dir[1], dir[0]];

        
        if (aprev > 0 && anext < 0 && normal[1] < 0
            || aprev < 0 && anext > 0 && normal[1] > 0) {
            normal[0] *= -1;
            normal[1] *= -1;
        } else if (Math.abs(aprev) < Math.abs(anext) && normal[0] < 0
                   || Math.abs(aprev) > Math.abs(anext) && normal[0] > 0) {
            normal[0] *= -1;
            normal[1] *= -1;
        }
        
        x = cur[0] + normal[0] * offsetFromViz;
        y = cur[1] + normal[1] * offsetFromViz;

        
        boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
        boxy = y - bbox.height /2 - offsetBox;
        boxw = bbox.width + 2 * offsetBox;
        boxh = bbox.height + 2 * offsetBox;

        
        
        if (boxx < clipRect[0] || (boxx + boxw) > (clipRect[0] + clipRect[2])) {
            normal[0] *= -1;
        }
        if (boxy < clipRect[1] || (boxy + boxh) > (clipRect[1] + clipRect[3])) {
            normal[1] *= -1;
        }

        
        x = cur[0] + normal[0] * offsetFromViz;
        y = cur[1] + normal[1] * offsetFromViz;

        
        boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
        boxy = y - bbox.height /2 - offsetBox;
        boxw = bbox.width + 2 * offsetBox;
        boxh = bbox.height + 2 * offsetBox;

        if (chart.animate) {
            
            me.onAnimate(callout.lines, {
                to: {
                    path: ["M", cur[0], cur[1], "L", x, y, "Z"]
                }
            });
            
            if (callout.panel) {
                callout.panel.setPosition(boxx, boxy, true);
            }
        }
        else {
            
            callout.lines.setAttributes({
                path: ["M", cur[0], cur[1], "L", x, y, "Z"]
            }, true);
            
            if (callout.panel) {
                callout.panel.setPosition(boxx, boxy);
            }
        }
        for (p in callout) {
            callout[p].show(true);
        }
    },

    isItemInPoint: function(x, y, item, i) {
        var me = this,
            items = me.items,
            tolerance = me.selectionTolerance,
            result = null,
            prevItem,
            nextItem,
            prevPoint,
            nextPoint,
            ln,
            x1,
            y1,
            x2,
            y2,
            xIntersect,
            yIntersect,
            dist1, dist2, dist, midx, midy,
            sqrt = Math.sqrt, abs = Math.abs;

        nextItem = items[i];
        prevItem = i && items[i - 1];

        if (i >= ln) {
            prevItem = items[ln - 1];
        }
        prevPoint = prevItem && prevItem.point;
        nextPoint = nextItem && nextItem.point;
        x1 = prevItem ? prevPoint[0] : nextPoint[0] - tolerance;
        y1 = prevItem ? prevPoint[1] : nextPoint[1];
        x2 = nextItem ? nextPoint[0] : prevPoint[0] + tolerance;
        y2 = nextItem ? nextPoint[1] : prevPoint[1];
        dist1 = sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));
        dist2 = sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
        dist = Math.min(dist1, dist2);

        if (dist <= tolerance) {
            return dist == dist1? prevItem : nextItem;
        }
        return false;
    },

    
    toggleAll: function(show) {
        var me = this,
            i, ln, shadow, shadows;
        if (!show) {
            Ext.chart.series.Cartesian.prototype.hideAll.call(me);
        }
        else {
            Ext.chart.series.Cartesian.prototype.showAll.call(me);
        }
        if (me.line) {
            me.line.setAttributes({
                hidden: !show
            }, true);
            
            if (me.line.shadows) {
                for (i = 0, shadows = me.line.shadows, ln = shadows.length; i < ln; i++) {
                    shadow = shadows[i];
                    shadow.setAttributes({
                        hidden: !show
                    }, true);
                }
            }
        }
        if (me.fillPath) {
            me.fillPath.setAttributes({
                hidden: !show
            }, true);
        }
    },

    
    hideAll: function() {
        this.toggleAll(false);
    },

    
    showAll: function() {
        this.toggleAll(true);
    }
});


Ext.define('Ext.chart.series.Pie', {

    

    alternateClassName: ['Ext.chart.PieSeries', 'Ext.chart.PieChart'],

    extend:  Ext.chart.series.Series ,

    

    type: "pie",

    alias: 'series.pie',

    accuracy: 100000,

    rad: Math.PI * 2 / 100000,

    
    highlightDuration: 150,

    
    angleField: false,

    

    

    
    lengthField: false,

    
    donut: false,

    
    showInLegend: false,

    

    
    style: {},

    constructor: function(config) {
        this.callParent(arguments);
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            store = chart.store,
            shadow = chart.shadow, i, l, cfg;
        config.highlightCfg = Ext.merge({
            segment: {
                margin: 20
            }
        }, config.highlight);
        Ext.apply(me, config, {
            shadowAttributes: [{
                "stroke-width": 6,
                "stroke-opacity": 1,
                stroke: 'rgb(200, 200, 200)',
                translate: {
                    x: 1.2,
                    y: 2
                }
            },
            {
                "stroke-width": 4,
                "stroke-opacity": 1,
                stroke: 'rgb(150, 150, 150)',
                translate: {
                    x: 0.9,
                    y: 1.5
                }
            },
            {
                "stroke-width": 2,
                "stroke-opacity": 1,
                stroke: 'rgb(100, 100, 100)',
                translate: {
                    x: 0.6,
                    y: 1
                }
            }]
        });
        me.group = surface.getGroup(me.seriesId);
        if (shadow) {
            for (i = 0, l = me.shadowAttributes.length; i < l; i++) {
                me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
            }
        }
        surface.customAttributes.segment = function(opt) {
            
            
            
            var ans = me.getSegment(opt);
            if (!ans.path || ans.path.length === 0) {
                ans.path = ['M', 0, 0];
            }
            return ans;
        };
        me.__excludes = me.__excludes || [];
    },
    
    onRedraw: function(){
        this.initialize();
    },

    
    initialize: function() {
        var me = this,
            store = me.chart.getChartStore(),
            data = store.data.items,
            i, ln, rec;
            
        me.callParent();
        
        me.yField = [];
        if (me.label.field) {
            for (i = 0, ln = data.length; i < ln; i++) {
                rec = data[i];
                me.yField.push(rec.get(me.label.field));
            }
        }
    },

    
    getSegment: function(opt) {
        var me = this,
            rad = me.rad,
            cos = Math.cos,
            sin = Math.sin,
            x = me.centerX,
            y = me.centerY,
            x1 = 0, x2 = 0, x3 = 0, x4 = 0,
            y1 = 0, y2 = 0, y3 = 0, y4 = 0,
            x5 = 0, y5 = 0, x6 = 0, y6 = 0,
            delta = 1e-2,
            startAngle = opt.startAngle,
            endAngle = opt.endAngle,
            midAngle = (startAngle + endAngle) / 2 * rad,
            margin = opt.margin || 0,
            a1 = Math.min(startAngle, endAngle) * rad,
            a2 = Math.max(startAngle, endAngle) * rad,
            c1 = cos(a1), s1 = sin(a1),
            c2 = cos(a2), s2 = sin(a2),
            cm = cos(midAngle), sm = sin(midAngle),
            flag = 0, hsqr2 = 0.7071067811865476; 

        if (a2 - a1 < delta) {
            return {path: ""};
        }

        if (margin !== 0) {
            x += margin * cm;
            y += margin * sm;
        }

        x2 = x + opt.endRho * c1;
        y2 = y + opt.endRho * s1;

        x4 = x + opt.endRho * c2;
        y4 = y + opt.endRho * s2;

        x6 = x + opt.endRho * cm;
        y6 = y + opt.endRho * sm;

        if (opt.startRho !== 0) {
            x1 = x + opt.startRho * c1;
            y1 = y + opt.startRho * s1;
    
            x3 = x + opt.startRho * c2;
            y3 = y + opt.startRho * s2;
    
            x5 = x + opt.startRho * cm;
            y5 = y + opt.startRho * sm;

            return {
                path: [
                    ["M", x2, y2],
                    ["A", opt.endRho, opt.endRho, 0, 0, 1, x6, y6], ["L", x6, y6],
                    ["A", opt.endRho, opt.endRho, 0, flag, 1, x4, y4], ["L", x4, y4],
                    ["L", x3, y3],
                    ["A", opt.startRho, opt.startRho, 0, flag, 0, x5, y5], ["L", x5, y5],
                    ["A", opt.startRho, opt.startRho, 0, 0, 0, x1, y1], ["L", x1, y1],
                    ["Z"]
                ]
            };
        } else {
            return {
                path: [
                    ["M", x, y],
                    ["L", x2, y2],
                    ["A", opt.endRho, opt.endRho, 0, 0, 1, x6, y6], ["L", x6, y6],
                    ["A", opt.endRho, opt.endRho, 0, flag, 1, x4, y4], ["L", x4, y4],
                    ["L", x, y],
                    ["Z"]
                ]
            };
        }
    },

    
    calcMiddle: function(item) {
        var me = this,
            rad = me.rad,
            slice = item.slice,
            x = me.centerX,
            y = me.centerY,
            startAngle = slice.startAngle,
            endAngle = slice.endAngle,
            donut = +me.donut,
            midAngle = -(startAngle + endAngle) * rad / 2,
            r = (item.endRho + item.startRho) / 2,
            xm = x + r * Math.cos(midAngle),
            ym = y - r * Math.sin(midAngle);

        item.middle = {
            x: xm,
            y: ym
        };
    },

    
    drawSeries: function() {
        var me = this,
            store = me.chart.getChartStore(),
            data = store.data.items,
            record,
            group = me.group,
            animate = me.chart.animate,
            field = me.angleField || me.field || me.xField,
            lenField = [].concat(me.lengthField),
            totalLenField = 0,
            chart = me.chart,
            surface = chart.surface,
            chartBBox = chart.chartBBox,
            enableShadows = chart.shadow,
            shadowGroups = me.shadowGroups,
            shadowAttributes = me.shadowAttributes,
            lnsh = shadowGroups.length,
            layers = lenField.length,
            rhoAcum = 0,
            donut = +me.donut,
            layerTotals = [],
            items = [],
            totalField = 0,
            maxLenField = 0,
            angle = 0,
            seriesStyle = me.seriesStyle,
            colorArrayStyle = me.colorArrayStyle,
            colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0,
            rendererAttributes,
            shadowAttr,
            shadows,
            shadow,
            shindex,
            centerX,
            centerY,
            deltaRho,
            first = 0,
            slice,
            slices,
            sprite,
            value,
            item,
            lenValue,
            ln,
            i,
            j,
            endAngle,
            path,
            p,
            spriteOptions, bbox;

        Ext.apply(seriesStyle, me.style || {});

        me.setBBox();
        bbox = me.bbox;

        
        if (me.colorSet) {
            colorArrayStyle = me.colorSet;
            colorArrayLength = colorArrayStyle.length;
        }

        
        if (!store || !store.getCount() || me.seriesIsHidden) {
            me.hide();
            me.items = [];
            return;
        }

        me.unHighlightItem();
        me.cleanHighlights();

        centerX = me.centerX = chartBBox.x + (chartBBox.width / 2);
        centerY = me.centerY = chartBBox.y + (chartBBox.height / 2);
        me.radius = Math.min(centerX - chartBBox.x, centerY - chartBBox.y);
        me.slices = slices = [];
        me.items = items = [];

        for (i = 0, ln = data.length; i < ln; i++) {
            record = data[i];
            if (this.__excludes && this.__excludes[i]) {
                
                continue;
            }
            totalField += +record.get(field);
            if (lenField[0]) {
                for (j = 0, totalLenField = 0; j < layers; j++) {
                    totalLenField += +record.get(lenField[j]);
                }
                layerTotals[i] = totalLenField;
                maxLenField = Math.max(maxLenField, totalLenField);
            }
        }

        totalField = totalField || 1;
        for (i = 0, ln = data.length; i < ln; i++) {
            record = data[i];
            if (this.__excludes && this.__excludes[i]) {
                value = 0;
            } else {
                value = record.get(field);
                if (first === 0) {
                    first = 1;
                }
            }

            
            if (first == 1) {
                first = 2;
                me.firstAngle = angle = me.accuracy * value / totalField / 2;
                for (j = 0; j < i; j++) {
                    slices[j].startAngle = slices[j].endAngle = me.firstAngle;
                }
            }

            endAngle = angle - me.accuracy * value / totalField;
            slice = {
                series: me,
                value: value,
                startAngle: angle,
                endAngle: endAngle,
                storeItem: record
            };
            if (lenField[0]) {
                lenValue = +layerTotals[i];
                
                slice.rho = Math.floor(me.radius / maxLenField * lenValue);
            } else {
                slice.rho = me.radius;
            }
            slices[i] = slice;
            
            (function () {
                angle = endAngle;
            })();
        }

        
        if (enableShadows) {
            for (i = 0, ln = slices.length; i < ln; i++) {
                slice = slices[i];
                slice.shadowAttrs = [];
                for (j = 0, rhoAcum = 0, shadows = []; j < layers; j++) {
                    sprite = group.getAt(i * layers + j);
                    deltaRho = lenField[j] ? store.getAt(i).get(lenField[j]) / layerTotals[i] * slice.rho: slice.rho;
                    
                    rendererAttributes = {
                        segment: {
                            startAngle: slice.startAngle,
                            endAngle: slice.endAngle,
                            margin: 0,
                            rho: slice.rho,
                            startRho: rhoAcum + (deltaRho * donut / 100),
                            endRho: rhoAcum + deltaRho
                        },
                        hidden: !slice.value && (slice.startAngle % me.accuracy) == (slice.endAngle % me.accuracy)
                    };
                    
                    for (shindex = 0, shadows = []; shindex < lnsh; shindex++) {
                        shadowAttr = shadowAttributes[shindex];
                        shadow = shadowGroups[shindex].getAt(i);
                        if (!shadow) {
                            shadow = chart.surface.add(Ext.apply({}, {
                                type: 'path',
                                group: shadowGroups[shindex],
                                strokeLinejoin: "round"
                            }, rendererAttributes, shadowAttr));
                        }
                        shadowAttr = me.renderer(shadow, store.getAt(i), Ext.apply({}, rendererAttributes, shadowAttr), i, store);
                        if (animate) {
                            me.onAnimate(shadow, {
                                to: shadowAttr
                            });
                        } else {
                            shadow.setAttributes(shadowAttr, true);
                        }
                        shadows.push(shadow);
                    }
                    slice.shadowAttrs[j] = shadows;
                }
            }
        }
        
        for (i = 0, ln = slices.length; i < ln; i++) {
            slice = slices[i];
            for (j = 0, rhoAcum = 0; j < layers; j++) {
                sprite = group.getAt(i * layers + j);
                deltaRho = lenField[j] ? store.getAt(i).get(lenField[j]) / layerTotals[i] * slice.rho: slice.rho;
                
                rendererAttributes = Ext.apply({
                    segment: {
                        startAngle: slice.startAngle,
                        endAngle: slice.endAngle,
                        margin: 0,
                        rho: slice.rho,
                        startRho: rhoAcum + (deltaRho * donut / 100),
                        endRho: rhoAcum + deltaRho
                    },
                    hidden: (!slice.value && (slice.startAngle % me.accuracy) == (slice.endAngle % me.accuracy))
                }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[(layers > 1? j : i) % colorArrayLength] } || {}));
                item = Ext.apply({},
                rendererAttributes.segment, {
                    slice: slice,
                    series: me,
                    storeItem: slice.storeItem,
                    index: i
                });
                me.calcMiddle(item);
                if (enableShadows) {
                    item.shadows = slice.shadowAttrs[j];
                }
                items[i] = item;
                
                if (!sprite) {
                    spriteOptions = Ext.apply({
                        type: "path",
                        group: group,
                        middle: item.middle
                    }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[(layers > 1? j : i) % colorArrayLength] } || {}));
                    sprite = surface.add(Ext.apply(spriteOptions, rendererAttributes));
                }
                slice.sprite = slice.sprite || [];
                item.sprite = sprite;
                slice.sprite.push(sprite);
                slice.point = [item.middle.x, item.middle.y];
                if (animate) {
                    rendererAttributes = me.renderer(sprite, store.getAt(i), rendererAttributes, i, store);
                    sprite._to = rendererAttributes;
                    sprite._animating = true;
                    me.onAnimate(sprite, {
                        to: rendererAttributes,
                        listeners: {
                            afteranimate: {
                                fn: function() {
                                    this._animating = false;
                                },
                                scope: sprite
                            }
                        }
                    });
                } else {
                    rendererAttributes = me.renderer(sprite, store.getAt(i), Ext.apply(rendererAttributes, {
                        hidden: false
                    }), i, store);
                    sprite.setAttributes(rendererAttributes, true);
                }
                rhoAcum += deltaRho;
            }
        }

        
        ln = group.getCount();
        for (i = 0; i < ln; i++) {
            if (!slices[(i / layers) >> 0] && group.getAt(i)) {
                group.getAt(i).hide(true);
            }
        }
        if (enableShadows) {
            lnsh = shadowGroups.length;
            for (shindex = 0; shindex < ln; shindex++) {
                if (!slices[(shindex / layers) >> 0]) {
                    for (j = 0; j < lnsh; j++) {
                        if (shadowGroups[j].getAt(shindex)) {
                            shadowGroups[j].getAt(shindex).hide(true);
                        }
                    }
                }
            }
        }
        me.renderLabels();
        me.renderCallouts();
    },

    setSpriteAttributes: function(sprite, attrs, animate) {
        var me = this;
        if (animate) {
            sprite.stopAnimation();
            sprite.animate({
                to: attrs,
                duration: me.highlightDuration
            });
        }
        else {
            sprite.setAttributes(attrs, true);
        }
    },

    createLabelLine: function(i) {
        var me = this;
            calloutLine = me.label.calloutLine,
            line = me.chart.surface.add({
                type: 'path',
                stroke: (i === undefined ? '#555' : ((calloutLine && calloutLine.color) || me.getLegendColor(i))),
                lineWidth: (calloutLine && calloutLine.width) || 2,
                path: 'M0,0Z'
            });
        return line;
    },

    drawLabelLine: function(label, from, to, animate) {
        var me = this,
            line = label.lineSprite,
            path = 'M' + from.x + ' ' + from.y + 'L' + to.x + ' ' + to.y + 'Z';

        me.setSpriteAttributes(line, { 
            'path': path 
        }, animate);
    },

    
    onCreateLabel: function(storeItem, item, i, display) {
        var me = this,
            group = me.labelsGroup,
            config = me.label,
            centerX = me.centerX,
            centerY = me.centerY,
            middle = item.middle,
            endLabelStyle = Ext.apply(me.seriesLabelStyle || {}, config || {});

        return me.chart.surface.add(Ext.apply({
            'type': 'text',
            'text-anchor': 'middle',
            'group': group,
            'x': middle.x,
            'y': middle.y
        }, endLabelStyle));
    },

    
    onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {
        var me = this,
            chart = me.chart,
            resizing = chart.resizing,
            config = me.label,
            format = config.renderer,
            field = config.field,
            centerX = me.centerX,
            centerY = me.centerY,
            middle = item.middle,
            opt = {
                x: middle.x,
                y: middle.y
            },
            x = middle.x - centerX,
            y = middle.y - centerY,
            from = {},
            rho = 1,
            theta = Math.atan2(y, x || 1),
            dg = Ext.draw.Draw.degrees(theta),
            prevDg, labelBox, width, height,
            isOutside = (display === 'outside'),
            calloutLine = label.attr.calloutLine,
            lineWidth = (calloutLine && calloutLine.width) || 2,
            labelPadding = (label.attr.padding || 20) + (isOutside ? lineWidth/2 + 4 : 0),
            labelPaddingX = 0, labelPaddingY = 0;

        opt.hidden = false;

        if (this.__excludes && this.__excludes[i]) {
            opt.hidden = true;
        }

        label.setAttributes({
            opacity: (opt.hidden ? 0 : 1),
            text: format(storeItem.get(field), label, storeItem, item, i, display, animate, index)
        }, true);

        if (label.lineSprite) {
            var attrs = { opacity: (opt.hidden ? 0 : 1) };
            if (opt.hidden) {
                attrs.translate = {x:0, y:0};
            }
            me.setSpriteAttributes(label.lineSprite, attrs, false);
        }

        switch (display) {
        case 'outside':
            label.isOutside = true;

            
            rho = item.endRho;

            
            labelPaddingX = (Math.abs(dg) <= 90 ? labelPadding : -labelPadding);
            labelPaddingY = (dg >= 0 ? labelPadding : -labelPadding);

            
            label.setAttributes({rotation:{degrees: 0}}, true);
            labelBox = label.getBBox();
            width = labelBox.width/2 * Math.cos(theta);
            height = labelBox.height/2 * Math.sin(theta);
            width += labelPaddingX;
            height += labelPaddingY;

            rho += Math.sqrt(width*width + height*height);

            
            opt.x = rho * Math.cos(theta) + centerX;
            opt.y = rho * Math.sin(theta) + centerY;
            break;

        case 'rotate':
            dg = Ext.draw.Draw.normalizeDegrees(dg);
            dg = (dg > 90 && dg < 270) ? dg + 180: dg;

            prevDg = label.attr.rotation.degrees;
            if (prevDg != null && Math.abs(prevDg - dg) > 180 * 0.5) {
                if (dg > prevDg) {
                    dg -= 360;
                } else {
                    dg += 360;
                }
                dg = dg % 360;
            } else {
                dg = Ext.draw.Draw.normalizeDegrees(dg);
            }
            
            opt.rotate = {
                degrees: dg,
                x: opt.x,
                y: opt.y
            };
            break;

        default:
            break;
        }

        
        opt.translate = {
            x: 0, y: 0
        };
        if (animate && !resizing && (display != 'rotate' || prevDg != null)) {
            me.onAnimate(label, {
                to: opt
            });
        } else {
            label.setAttributes(opt, true);
        }
        label._from = from;

        
        if (label.isOutside && calloutLine) {
            var line = label.lineSprite,
                animateLine = animate,
                fromPoint = {
                    
                    x: (item.endRho - lineWidth/2) * Math.cos(theta) + centerX,
                    y: (item.endRho - lineWidth/2) * Math.sin(theta) + centerY
                },
                labelCenter = {
                    
                    x: opt.x,
                    y: opt.y
                },
                toPoint = {};

            function sign(x) {
                return x ? x < 0 ? -1 : 1 : 0;
            }

            if (calloutLine && calloutLine.length) {
                toPoint = {
                    x: (item.endRho + calloutLine.length) * Math.cos(theta) + centerX,
                    y: (item.endRho + calloutLine.length) * Math.sin(theta) + centerY
                }
            } else {
                
                
                
                
                
                
                var normalTheta = Ext.draw.Draw.normalizeRadians(-theta),
                    cos = Math.cos(normalTheta),
                    sin = Math.sin(normalTheta),
                    labelWidth = (labelBox.width + lineWidth + 4)/2,
                    labelHeight = (labelBox.height + lineWidth + 4)/2;

                if (Math.abs(cos) * labelHeight > Math.abs(sin) * labelWidth) {
                    
                    toPoint.x = labelCenter.x - labelWidth * sign(cos);
                    toPoint.y = labelCenter.y + labelWidth * sin/cos * sign(cos);
                } else {
                    
                    toPoint.x = labelCenter.x - labelHeight * cos/sin * sign(sin);
                    toPoint.y = labelCenter.y + labelHeight * sign(sin);
                }
            }

            if (!line) {
                line = label.lineSprite = me.createLabelLine(i);
                animateLine = false;
            }
            me.drawLabelLine(label, fromPoint, toPoint, animateLine);
        } else {
            delete label.lineSprite;
        }
    },

    
    onPlaceCallout: function(callout, storeItem, item, i, display, animate, index) {
        var me = this,
            chart = me.chart,
            centerX = me.centerX,
            centerY = me.centerY,
            middle = item.middle,
            opt = {
                x: middle.x,
                y: middle.y
            },
            x = middle.x - centerX,
            y = middle.y - centerY,
            rho = 1,
            rhoCenter,
            theta = Math.atan2(y, x || 1),
            bbox = (callout && callout.label ? callout.label.getBBox() : {width:0,height:0}),
            offsetFromViz = 20,
            offsetToSide = 10,
            offsetBox = 10,
            p;

        if (!bbox.width || !bbox.height) {
            return;
        }

        
        rho = item.endRho + offsetFromViz;
        rhoCenter = (item.endRho + item.startRho) / 2 + (item.endRho - item.startRho) / 3;
        
        opt.x = rho * Math.cos(theta) + centerX;
        opt.y = rho * Math.sin(theta) + centerY;

        x = rhoCenter * Math.cos(theta);
        y = rhoCenter * Math.sin(theta);

        if (chart.animate) {
            
            me.onAnimate(callout.lines, {
                to: {
                    path: ["M", x + centerX, y + centerY, "L", opt.x, opt.y, "Z", "M", opt.x, opt.y, "l", x > 0 ? offsetToSide: -offsetToSide, 0, "z"]
                }
            });
            
            me.onAnimate(callout.box, {
                to: {
                    x: opt.x + (x > 0 ? offsetToSide: -(offsetToSide + bbox.width + 2 * offsetBox)),
                    y: opt.y + (y > 0 ? ( - bbox.height - offsetBox / 2) : ( - bbox.height - offsetBox / 2)),
                    width: bbox.width + 2 * offsetBox,
                    height: bbox.height + 2 * offsetBox
                }
            });
            
            me.onAnimate(callout.label, {
                to: {
                    x: opt.x + (x > 0 ? (offsetToSide + offsetBox) : -(offsetToSide + bbox.width + offsetBox)),
                    y: opt.y + (y > 0 ? -bbox.height / 4: -bbox.height / 4)
                }
            });
        } else {
            
            callout.lines.setAttributes({
                path: ["M", x + centerX, y + centerY, "L", opt.x, opt.y, "Z", "M", opt.x, opt.y, "l", x > 0 ? offsetToSide: -offsetToSide, 0, "z"]
            },
            true);
            
            callout.box.setAttributes({
                x: opt.x + (x > 0 ? offsetToSide: -(offsetToSide + bbox.width + 2 * offsetBox)),
                y: opt.y + (y > 0 ? ( - bbox.height - offsetBox / 2) : ( - bbox.height - offsetBox / 2)),
                width: bbox.width + 2 * offsetBox,
                height: bbox.height + 2 * offsetBox
            },
            true);
            
            callout.label.setAttributes({
                x: opt.x + (x > 0 ? (offsetToSide + offsetBox) : -(offsetToSide + bbox.width + offsetBox)),
                y: opt.y + (y > 0 ? -bbox.height / 4: -bbox.height / 4)
            },
            true);
        }
        for (p in callout) {
            callout[p].show(true);
        }
    },

    
    onAnimate: function(sprite, attr) {
        sprite.show();
        return this.callParent(arguments);
    },

    isItemInPoint: function(x, y, item, i) {
        var me = this,
            cx = me.centerX,
            cy = me.centerY,
            abs = Math.abs,
            dx = abs(x - cx),
            dy = abs(y - cy),
            startAngle = item.startAngle,
            endAngle = item.endAngle,
            rho = Math.sqrt(dx * dx + dy * dy),
            angle = Math.atan2(y - cy, x - cx) / me.rad;

        
        if (angle > me.firstAngle) {
            angle -= me.accuracy;
        }
        return (angle <= startAngle && angle > endAngle
                && rho >= item.startRho && rho <= item.endRho);
    },

    
    hideAll: function(index) {
        var i, l, shadow, shadows, sh, lsh, sprite;
        index = (isNaN(this._index) ? index : this._index) || 0;
        this.__excludes = this.__excludes || [];
        this.__excludes[index] = true;
        sprite = this.slices[index].sprite;
        for (sh = 0, lsh = sprite.length; sh < lsh; sh++) {
            sprite[sh].setAttributes({
                hidden: true
            }, true);
            var line = sprite[sh].lineSprite;
            if (line) {
                line.setAttributes({
                    hidden: true
                }, true);
            }
        }
        if (this.slices[index].shadowAttrs) {
            for (i = 0, shadows = this.slices[index].shadowAttrs, l = shadows.length; i < l; i++) {
                shadow = shadows[i];
                for (sh = 0, lsh = shadow.length; sh < lsh; sh++) {
                    shadow[sh].setAttributes({
                        hidden: true
                    }, true);
                }
            }
        }
        this.drawSeries();
    },

    
    showAll: function(index) {
        index = (isNaN(this._index) ? index : this._index) || 0;
        this.__excludes[index] = false;
        this.drawSeries();
    },

    
    highlightItem: function(item) {
        var me = this,
            rad = me.rad,
            highlightSegment,
            animate,
            attrs,
            i,
            shadows,
            shadow,
            ln,
            to,
            itemHighlightSegment,
            prop,
            group,
            display,
            label,
            middle,
            r,
            x,
            y,
            line;
        item = item || this.items[this._index];

        
        
        
        
        this.unHighlightItem();

        if (!item || me.animating || (item.sprite && item.sprite._animating)) {
            return;
        }
        me.callParent([item]);
        if (!me.highlight) {
            return;
        }
        if ('segment' in me.highlightCfg) {
            highlightSegment = me.highlightCfg.segment;
            animate = me.chart.animate;
            
            if (me.labelsGroup) {
                group = me.labelsGroup;
                display = me.label.display;
                label = group.getAt(item.index);
                middle = (item.startAngle + item.endAngle) / 2 * rad;
                r = highlightSegment.margin || 0;
                x = r * Math.cos(middle);
                y = r * Math.sin(middle);

                
                
                
                
                
                if (Math.abs(x) < 1e-10) {
                    x = 0;
                }
                if (Math.abs(y) < 1e-10) {
                    y = 0;
                }

                me.setSpriteAttributes(label, { translate: {x:x, y:y}}, animate);

                line = label.lineSprite;
                if (line) {
                    me.setSpriteAttributes(line, { translate: {x:x, y:y}}, animate);
                }
            }
            
            if (me.chart.shadow && item.shadows) {
                i = 0;
                shadows = item.shadows;
                ln = shadows.length;
                for (; i < ln; i++) {
                    shadow = shadows[i];
                    to = {};
                    itemHighlightSegment = item.sprite._from.segment;
                    for (prop in itemHighlightSegment) {
                        if (! (prop in highlightSegment)) {
                            to[prop] = itemHighlightSegment[prop];
                        }
                    }
                    attrs = {
                        segment: Ext.applyIf(to, me.highlightCfg.segment)
                    };
                    me.setSpriteAttributes(shadow, attrs, animate);
                }
            }
        }
    },

    
    unHighlightItem: function() {
        var me = this,
            items,
            animate,
            shadowsEnabled,
            group,
            len,
            i,
            j,
            display,
            shadowLen,
            p,
            to,
            ihs,
            hs,
            sprite,
            shadows,
            shadow,
            item,
            label,
            attrs;
        if (!me.highlight) {
            return;
        }

        if (('segment' in me.highlightCfg) && me.items) {
            items = me.items;
            animate = me.chart.animate;
            shadowsEnabled = !!me.chart.shadow;
            group = me.labelsGroup;
            len = items.length;
            i = 0;
            j = 0;
            display = me.label.display;

            for (; i < len; i++) {
                item = items[i];
                if (!item) {
                    continue;
                }
                sprite = item.sprite;
                if (sprite && sprite._highlighted) {
                    
                    if (group) {
                        label = group.getAt(item.index);
                        attrs = Ext.apply({
                            translate: {
                                x: 0,
                                y: 0
                            }
                        },
                        display == 'rotate' ? {
                            rotate: {
                                x: label.attr.x,
                                y: label.attr.y,
                                degrees: label.attr.rotation.degrees
                            }
                        }: {});

                        me.setSpriteAttributes(label, attrs, animate);

                        var line = label.lineSprite;
                        if (line) {
                            me.setSpriteAttributes(line, { translate: {x:0, y:0}}, animate);
                        }
                    }
                    if (shadowsEnabled) {
                        shadows = item.shadows;
                        shadowLen = shadows.length;
                        for (; j < shadowLen; j++) {
                            to = {};
                            ihs = item.sprite._to.segment;
                            hs = item.sprite._from.segment;
                            Ext.apply(to, hs);
                            for (p in ihs) {
                                if (! (p in hs)) {
                                    to[p] = ihs[p];
                                }
                            }
                            shadow = shadows[j];
                            me.setSpriteAttributes(shadow, { segment: to }, animate);
                        }
                    }
                }
            }
        }
        me.callParent(arguments);
    },

    
    getLegendColor: function(index) {
        var me = this;
        return (me.colorSet && me.colorSet[index % me.colorSet.length]) || me.colorArrayStyle[index % me.colorArrayStyle.length];
    }
});



Ext.define('Ext.chart.series.Radar', {

    

    extend:  Ext.chart.series.Series ,

                                                 

    

    type: "radar",
    alias: 'series.radar',


    rad: Math.PI / 180,

    showInLegend: false,

    
    style: {},

    

    

    

    

    constructor: function(config) {
        this.callParent(arguments);
        var me = this,
            surface = me.chart.surface;
        me.group = surface.getGroup(me.seriesId);
        if (me.showMarkers) {
            me.markerGroup = surface.getGroup(me.seriesId + '-markers');
        }
    },

    
    drawSeries: function() {
        var me = this,
            store = me.chart.getChartStore(),
            data = store.data.items,
            d, record,
            group = me.group,
            chart = me.chart,
            seriesItems = chart.series.items,
            s, sLen, series,
            field = me.field || me.yField,
            surface = chart.surface,
            chartBBox = chart.chartBBox,
            colorArrayStyle = me.colorArrayStyle,
            centerX, centerY,
            items,
            radius,
            maxValue = 0,
            fields = [],
            max = Math.max,
            cos = Math.cos,
            sin = Math.sin,
            pi2 = Math.PI * 2,
            l = store.getCount(),
            startPath, path, x, y, rho,
            i, nfields,
            seriesStyle = me.seriesStyle,
            axis = chart.axes && chart.axes.get(0),
            aggregate = !(axis && axis.maximum);

        me.setBBox();
        
        maxValue = aggregate? 0 : (axis.maximum || 0);
        
        Ext.apply(seriesStyle, me.style || {});

        
        if (!store || !store.getCount() || me.seriesIsHidden) {
            me.hide();
            me.items = [];
            if (me.radar) {
                me.radar.hide(true);
            }
            me.radar = null;
            return;
        }
        
        if(!seriesStyle['stroke']){
            seriesStyle['stroke'] = colorArrayStyle[me.themeIdx % colorArrayStyle.length];
        }

        me.unHighlightItem();
        me.cleanHighlights();

        centerX = me.centerX = chartBBox.x + (chartBBox.width / 2);
        centerY = me.centerY = chartBBox.y + (chartBBox.height / 2);
        me.radius = radius = Math.min(chartBBox.width, chartBBox.height) /2;
        me.items = items = [];

        if (aggregate) {
            
            for (s = 0, sLen = seriesItems.length; s < sLen; s++) {
                series = seriesItems[s];
                fields.push(series.yField);
            }
            
            for (d = 0; d < l; d++) {
                record = data[d];
                for (i = 0, nfields = fields.length; i < nfields; i++) {
                    maxValue = max(+record.get(fields[i]), maxValue);
                }
            }
        }
        
        maxValue = maxValue || 1;
        
        startPath = []; path = [];
        for (i = 0; i < l; i++) {
            record = data[i];
            rho = radius * record.get(field) / maxValue;
            x = rho * cos(i / l * pi2);
            y = rho * sin(i / l * pi2);
            if (i == 0) {
                path.push('M', x + centerX, y + centerY);
                startPath.push('M', 0.01 * x + centerX, 0.01 * y + centerY);
            } else {
                path.push('L', x + centerX, y + centerY);
                startPath.push('L', 0.01 * x + centerX, 0.01 * y + centerY);
            }
            items.push({
                sprite: false, 
                point: [centerX + x, centerY + y],
                storeItem: record,
                series: me
            });
        }
        path.push('Z');
        
        if (!me.radar) {
            me.radar = surface.add(Ext.apply({
                type: 'path',
                group: group,
                path: startPath
            }, seriesStyle || {}));
        }
        
        if (chart.resizing) {
            me.radar.setAttributes({
                path: startPath
            }, true);
        }
        
        if (chart.animate) {
            me.onAnimate(me.radar, {
                to: Ext.apply({
                    path: path
                }, seriesStyle || {})
            });
        } else {
            me.radar.setAttributes(Ext.apply({
                path: path
            }, seriesStyle || {}), true);
        }
        
        if (me.showMarkers) {
            me.drawMarkers();
        }
        me.renderLabels();
        me.renderCallouts();
    },

    
    drawMarkers: function() {
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            store = chart.getChartStore(),
            markerStyle = Ext.apply({}, me.markerStyle || {}),
            endMarkerStyle = Ext.apply(markerStyle, me.markerConfig, {
                fill: me.colorArrayStyle[me.themeIdx % me.colorArrayStyle.length]
            }),
            items = me.items,
            type = endMarkerStyle.type,
            markerGroup = me.markerGroup,
            centerX = me.centerX,
            centerY = me.centerY,
            item, i, l, marker, rendererAttributes;

        delete endMarkerStyle.type;

        for (i = 0, l = items.length; i < l; i++) {
            item = items[i];
            marker = markerGroup.getAt(i);
            if (!marker) {
                marker = Ext.chart.Shape[type](surface, Ext.apply({
                    group: markerGroup,
                    x: 0,
                    y: 0,
                    translate: {
                        x: centerX,
                        y: centerY
                    }
                }, endMarkerStyle));
            }
            else {
                marker.show();
            }

            item.sprite = marker;

            if (chart.resizing) {
                marker.setAttributes({
                    x: 0,
                    y: 0,
                    translate: {
                        x: centerX,
                        y: centerY
                    }
                }, true);
            }
            marker._to = {
                translate: {
                    x: item.point[0],
                    y: item.point[1]
                }
            };
            
            rendererAttributes = me.renderer(marker, store.getAt(i), marker._to, i, store);
            rendererAttributes = Ext.applyIf(rendererAttributes || {}, endMarkerStyle || {});
            if (chart.animate) {
                me.onAnimate(marker, {
                    to: rendererAttributes
                });
            }
            else {
                marker.setAttributes(rendererAttributes, true);
            }
        }
    },

    isItemInPoint: function(x, y, item) {
        var point,
            tolerance = 10,
            abs = Math.abs;
        point = item.point;
        return (abs(point[0] - x) <= tolerance &&
                abs(point[1] - y) <= tolerance);
    },

    
    onCreateLabel: function(storeItem, item, i, display) {
        var me = this,
            group = me.labelsGroup,
            config = me.label,
            centerX = me.centerX,
            centerY = me.centerY,
            endLabelStyle = Ext.apply({}, config, me.seriesLabelStyle || {});

        return me.chart.surface.add(Ext.apply({
            'type': 'text',
            'text-anchor': 'middle',
            'group': group,
            'x': centerX,
            'y': centerY
        }, endLabelStyle || {}));
    },

    
    onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {
        var me = this,
            chart = me.chart,
            resizing = chart.resizing,
            config = me.label,
            format = config.renderer,
            field = config.field,
            centerX = me.centerX,
            centerY = me.centerY,
            opt = {
                x: Number(item.point[0]),
                y: Number(item.point[1])
            },
            x = opt.x - centerX,
            y = opt.y - centerY,
            theta = Math.atan2(y, x || 1),
            deg = theta * 180 / Math.PI,
            labelBox, direction;

            function fixAngle(a) {
                if (a < 0) {
                    a += 360;
                }
                return a % 360;
            }

        label.setAttributes({
            text: format(storeItem.get(field), label, storeItem, item, i, display, animate, index),
            hidden: true
        },
        true);

        
        
        labelBox = label.getBBox();
        deg = fixAngle(deg);
        if ((deg > 45 && deg < 135) || (deg > 225 && deg < 315)) {
            direction = (deg > 45 && deg < 135 ? 1 : -1);
            opt.y += direction * labelBox.height/2;
        } else {
            direction = (deg >= 135 && deg <= 225 ? -1 : 1);
            opt.x += direction * labelBox.width/2;
        }

        if (resizing) {
            label.setAttributes({
                x: centerX,
                y: centerY
            }, true);
        }

        if (animate) {
            label.show(true);
            me.onAnimate(label, {
                to: opt
            });
        } else {
            label.setAttributes(opt, true);
            label.show(true);
        }
    },

    
    toggleAll: function(show) {
        var me = this,
            i, ln, shadow, shadows;
        if (!show) {
            Ext.chart.series.Radar.superclass.hideAll.call(me);
        }
        else {
            Ext.chart.series.Radar.superclass.showAll.call(me);
        }
        if (me.radar) {
            me.radar.setAttributes({
                hidden: !show
            }, true);
            
            if (me.radar.shadows) {
                for (i = 0, shadows = me.radar.shadows, ln = shadows.length; i < ln; i++) {
                    shadow = shadows[i];
                    shadow.setAttributes({
                        hidden: !show
                    }, true);
                }
            }
        }
    },

    
    hideAll: function() {
        this.toggleAll(false);
        this.hideMarkers(0);
    },

    
    showAll: function() {
        this.toggleAll(true);
    },

    
    hideMarkers: function(index) {
        var me = this,
            count = me.markerGroup && me.markerGroup.getCount() || 0,
            i = index || 0;
        for (; i < count; i++) {
            me.markerGroup.getAt(i).hide(true);
        }
    },

    
    
    getAxesForXAndYFields: function() {
        var me = this,
            chart = me.chart,
            axes = chart.axes,
            axis = [].concat(axes && axes.get(0));

        return {
            yAxis: axis
        };
    }
});



Ext.define('Ext.chart.series.Scatter', {

    

    extend:  Ext.chart.series.Cartesian ,

                                                                        

    

    type: 'scatter',
    alias: 'series.scatter',

    

    

    constructor: function(config) {
        this.callParent(arguments);
        var me = this,
            shadow = me.chart.shadow,
            surface = me.chart.surface, i, l;
        Ext.apply(me, config, {
            style: {},
            markerConfig: {},
            shadowAttributes: [{
                "stroke-width": 6,
                "stroke-opacity": 0.05,
                stroke: 'rgb(0, 0, 0)'
            }, {
                "stroke-width": 4,
                "stroke-opacity": 0.1,
                stroke: 'rgb(0, 0, 0)'
            }, {
                "stroke-width": 2,
                "stroke-opacity": 0.15,
                stroke: 'rgb(0, 0, 0)'
            }]
        });
        me.group = surface.getGroup(me.seriesId);
        if (shadow) {
            for (i = 0, l = me.shadowAttributes.length; i < l; i++) {
                me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
            }
        }
    },

    
    getBounds: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            chartAxes = chart.axes,
            boundAxes = me.getAxesForXAndYFields(),
            boundXAxis = boundAxes.xAxis,
            boundYAxis = boundAxes.yAxis,
            bbox, xScale, yScale, ln, minX, minY, maxX, maxY, i, axis, ends;

        me.setBBox();
        bbox = me.bbox;

        if (axis = chartAxes.get(boundXAxis)) {
            ends = axis.applyData();
            minX = ends.from;
            maxX = ends.to;
        }

        if (axis = chartAxes.get(boundYAxis)) {
            ends = axis.applyData();
            minY = ends.from;
            maxY = ends.to;
        }

        
        if (me.xField && !Ext.isNumber(minX)) {
            axis = me.getMinMaxXValues();
            minX = axis[0];
            maxX = axis[1];
        }

        if (me.yField && !Ext.isNumber(minY)) {
            axis = me.getMinMaxYValues();
            minY = axis[0];
            maxY = axis[1];
        }

        if (isNaN(minX)) {
            minX = 0;
            maxX = store.getCount() - 1;
            xScale = bbox.width / (store.getCount() - 1);
        }
        else {
            xScale = bbox.width / (maxX - minX);
        }

        if (isNaN(minY)) {
            minY = 0;
            maxY = store.getCount() - 1;
            yScale = bbox.height / (store.getCount() - 1);
        }
        else {
            yScale = bbox.height / (maxY - minY);
        }

        return {
            bbox: bbox,
            minX: minX,
            minY: minY,
            xScale: xScale,
            yScale: yScale
        };
    },

    
    getPaths: function() {
        var me = this,
            chart = me.chart,
            enableShadows = chart.shadow,
            store = chart.getChartStore(),
            data = store.data.items,
            i, ln, record,
            group = me.group,
            bounds = me.bounds = me.getBounds(),
            bbox = me.bbox,
            xScale = bounds.xScale,
            yScale = bounds.yScale,
            minX = bounds.minX,
            minY = bounds.minY,
            boxX = bbox.x,
            boxY = bbox.y,
            boxHeight = bbox.height,
            items = me.items = [],
            attrs = [],
            reverse = me.reverse,
            x, y, xValue, yValue, sprite;

        for (i = 0, ln = data.length; i < ln; i++) {
            record = data[i];
            xValue = record.get(me.xField);
            yValue = record.get(me.yField);
            
            
            if (typeof yValue == 'undefined' || (typeof yValue == 'string' && !yValue)
                || xValue == null || yValue == null) {
                continue;
            }
            
            if (typeof xValue == 'string' || typeof xValue == 'object' && !Ext.isDate(xValue)) {
                xValue = i;
            }
            if (typeof yValue == 'string' || typeof yValue == 'object' && !Ext.isDate(yValue)) {
                yValue = i;
            }
            if (reverse) {
                x = boxX + bbox.width - ((xValue - minX) * xScale);
            } else {
                x = boxX + (xValue - minX) * xScale;
            }
            
            y = boxY + boxHeight - (yValue - minY) * yScale;
            
            attrs.push({
                x: x,
                y: y
            });

            me.items.push({
                series: me,
                value: [xValue, yValue],
                point: [x, y],
                storeItem: record
            });

            
            if (chart.animate && chart.resizing) {
                sprite = group.getAt(i);
                if (sprite) {
                    me.resetPoint(sprite);
                    if (enableShadows) {
                        me.resetShadow(sprite);
                    }
                }
            }
        }
        return attrs;
    },

    
    resetPoint: function(sprite) {
        var bbox = this.bbox;
        sprite.setAttributes({
            translate: {
                x: (bbox.x + bbox.width) / 2,
                y: (bbox.y + bbox.height) / 2
            }
        }, true);
    },

    
    resetShadow: function(sprite) {
        var me = this,
            shadows = sprite.shadows,
            shadowAttributes = me.shadowAttributes,
            ln = me.shadowGroups.length,
            bbox = me.bbox,
            i, attr;
        for (i = 0; i < ln; i++) {
            attr = Ext.apply({}, shadowAttributes[i]);
            
            if (attr.translate) {
                attr.translate.x += (bbox.x + bbox.width) / 2;
                attr.translate.y += (bbox.y + bbox.height) / 2;
            }
            else {
                attr.translate = {
                    x: (bbox.x + bbox.width) / 2,
                    y: (bbox.y + bbox.height) / 2
                };
            }
            shadows[i].setAttributes(attr, true);
        }
    },

    
    createPoint: function(attr, type) {
        var me = this,
            chart = me.chart,
            group = me.group,
            bbox = me.bbox;

        return Ext.chart.Shape[type](chart.surface, Ext.apply({}, {
            x: 0,
            y: 0,
            group: group,
            translate: {
                x: (bbox.x + bbox.width) / 2,
                y: (bbox.y + bbox.height) / 2
            }
        }, attr));
    },

    
    createShadow: function(sprite, endMarkerStyle, type) {
        var me = this,
            chart = me.chart,
            shadowGroups = me.shadowGroups,
            shadowAttributes = me.shadowAttributes,
            lnsh = shadowGroups.length,
            bbox = me.bbox,
            i, shadow, shadows, attr;

        sprite.shadows = shadows = [];

        for (i = 0; i < lnsh; i++) {
            attr = Ext.apply({}, shadowAttributes[i]);
            if (attr.translate) {
                attr.translate.x += (bbox.x + bbox.width) / 2;
                attr.translate.y += (bbox.y + bbox.height) / 2;
            }
            else {
                Ext.apply(attr, {
                    translate: {
                        x: (bbox.x + bbox.width) / 2,
                        y: (bbox.y + bbox.height) / 2
                    }
                });
            }
            Ext.apply(attr, endMarkerStyle);
            shadow = Ext.chart.Shape[type](chart.surface, Ext.apply({}, {
                x: 0,
                y: 0,
                group: shadowGroups[i]
            }, attr));
            shadows.push(shadow);
        }
    },

    
    drawSeries: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            group = me.group,
            enableShadows = chart.shadow,
            shadowGroups = me.shadowGroups,
            shadowAttributes = me.shadowAttributes,
            lnsh = shadowGroups.length,
            sprite, attrs, attr, ln, i, endMarkerStyle, shindex, type, shadows,
            rendererAttributes, shadowAttribute;

        endMarkerStyle = Ext.apply(me.markerStyle, me.markerConfig);
        type = endMarkerStyle.type || 'circle';
        delete endMarkerStyle.type;

        
        if (!store || !store.getCount()) {
            me.hide();
            me.items = [];
            return;
        }


        me.unHighlightItem();
        me.cleanHighlights();

        attrs = me.getPaths();
        ln = attrs.length;
        for (i = 0; i < ln; i++) {
            attr = attrs[i];
            sprite = group.getAt(i);
            Ext.apply(attr, endMarkerStyle);

            
            if (!sprite) {
                sprite = me.createPoint(attr, type);
                if (enableShadows) {
                    me.createShadow(sprite, endMarkerStyle, type);
                }
            }

            shadows = sprite.shadows;
            if (chart.animate) {
                rendererAttributes = me.renderer(sprite, store.getAt(i), { translate: attr }, i, store);
                sprite._to = rendererAttributes;
                me.onAnimate(sprite, {
                    to: rendererAttributes
                });
                
                for (shindex = 0; shindex < lnsh; shindex++) {
                    shadowAttribute = Ext.apply({}, shadowAttributes[shindex]);
                    rendererAttributes = me.renderer(shadows[shindex], store.getAt(i), Ext.apply({}, { 
                        hidden: false,
                        translate: {
                            x: attr.x + (shadowAttribute.translate? shadowAttribute.translate.x : 0),
                            y: attr.y + (shadowAttribute.translate? shadowAttribute.translate.y : 0)
                        }
                    }, shadowAttribute), i, store);
                    me.onAnimate(shadows[shindex], { to: rendererAttributes });
                }
            }
            else {
                rendererAttributes = me.renderer(sprite, store.getAt(i), { translate: attr }, i, store);
                sprite._to = rendererAttributes;
                sprite.setAttributes(rendererAttributes, true);
                
                for (shindex = 0; shindex < lnsh; shindex++) {
                    shadowAttribute = Ext.apply({}, shadowAttributes[shindex]);
                    rendererAttributes = me.renderer(shadows[shindex], store.getAt(i), Ext.apply({}, { 
                        hidden: false,
                        translate: {
                            x: attr.x + (shadowAttribute.translate? shadowAttribute.translate.x : 0),
                            y: attr.y + (shadowAttribute.translate? shadowAttribute.translate.y : 0)
                        } 
                    }, shadowAttribute), i, store);
                    shadows[shindex].setAttributes(rendererAttributes, true);
                }
            }
            me.items[i].sprite = sprite;
        }

        
        ln = group.getCount();
        for (i = attrs.length; i < ln; i++) {
            group.getAt(i).hide(true);
        }
        me.renderLabels();
        me.renderCallouts();
    },

    
    onCreateLabel: function(storeItem, item, i, display) {
        var me = this,
            group = me.labelsGroup,
            config = me.label,
            endLabelStyle = Ext.apply({}, config, me.seriesLabelStyle),
            bbox = me.bbox;

        return me.chart.surface.add(Ext.apply({
            type: 'text',
            'text-anchor': 'middle',
            group: group,
            x: Number(item.point[0]),
            y: bbox.y + bbox.height / 2
        }, endLabelStyle));
    },

    
    onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {
        var me = this,
            chart = me.chart,
            resizing = chart.resizing,
            config = me.label,
            format = config.renderer,
            field = config.field,
            bbox = me.bbox,
            x = Number(item.point[0]),
            y = Number(item.point[1]),
            radius = item.sprite.attr.radius,
            labelBox, markerBox, width, height, xOffset, yOffset,
            anim;

        label.setAttributes({
            text: format(storeItem.get(field), label, storeItem, item, i, display, animate, index),
            hidden: true
        }, true);


        
        markerBox = item.sprite.getBBox();
        markerBox.width = markerBox.width || (radius * 2);
        markerBox.height = markerBox.height || (radius * 2);

        labelBox = label.getBBox();
        width = labelBox.width/2;
        height = labelBox.height/2;

        if (display == 'rotate') {
            
            xOffset = markerBox.width/2 + width + height/2;
            if (x + xOffset + width > bbox.x + bbox.width) {
                x -= xOffset;
            } else {
                x += xOffset;
            }
            label.setAttributes({
                'rotation': {
                    x: x,
                    y: y,
                    degrees: -45
                }
            }, true);
        } else if (display == 'under' || display == 'over') {
            label.setAttributes({
                'rotation': {
                    degrees: 0
                }
            }, true);

            
            if (x < bbox.x + width) {
                x = bbox.x + width;
            } else if (x + width > bbox.x + bbox.width) {
                x = bbox.x + bbox.width - width;
            }

            yOffset = markerBox.height/2 + height;
            y = y + (display == 'over' ? -yOffset : yOffset);
            if (y < bbox.y + height) {
                y += 2 * yOffset;
            } else if (y + height > bbox.y + bbox.height) {
                y -= 2 * yOffset;
            }
        }

        if (!chart.animate) {
            label.setAttributes({
                x: x,
                y: y
            }, true);
            label.show(true);
        }
        else {
            if (resizing) {
                anim = item.sprite.getActiveAnimation();
                if (anim) {
                    anim.on('afteranimate', function() {
                        label.setAttributes({
                            x: x,
                            y: y
                        }, true);
                        label.show(true);
                    });
                }
                else {
                    label.show(true);
                }
            }
            else {
                me.onAnimate(label, {
                    to: {
                        x: x,
                        y: y
                    }
                });
            }
        }
    },

    
    onPlaceCallout: function(callout, storeItem, item, i, display, animate, index) {
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            resizing = chart.resizing,
            config = me.callouts,
            items = me.items,
            cur = item.point,
            normal,
            bbox = callout.label.getBBox(),
            offsetFromViz = 30,
            offsetToSide = 10,
            offsetBox = 3,
            boxx, boxy, boxw, boxh,
            p, clipRect = me.bbox,
            x, y;

        
        normal = [Math.cos(Math.PI /4), -Math.sin(Math.PI /4)];
        x = cur[0] + normal[0] * offsetFromViz;
        y = cur[1] + normal[1] * offsetFromViz;

        
        boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
        boxy = y - bbox.height /2 - offsetBox;
        boxw = bbox.width + 2 * offsetBox;
        boxh = bbox.height + 2 * offsetBox;

        
        
        if (boxx < clipRect[0] || (boxx + boxw) > (clipRect[0] + clipRect[2])) {
            normal[0] *= -1;
        }
        if (boxy < clipRect[1] || (boxy + boxh) > (clipRect[1] + clipRect[3])) {
            normal[1] *= -1;
        }

        
        x = cur[0] + normal[0] * offsetFromViz;
        y = cur[1] + normal[1] * offsetFromViz;

        
        boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
        boxy = y - bbox.height /2 - offsetBox;
        boxw = bbox.width + 2 * offsetBox;
        boxh = bbox.height + 2 * offsetBox;

        if (chart.animate) {
            
            me.onAnimate(callout.lines, {
                to: {
                    path: ["M", cur[0], cur[1], "L", x, y, "Z"]
                }
            }, true);
            
            me.onAnimate(callout.box, {
                to: {
                    x: boxx,
                    y: boxy,
                    width: boxw,
                    height: boxh
                }
            }, true);
            
            me.onAnimate(callout.label, {
                to: {
                    x: x + (normal[0] > 0? offsetBox : -(bbox.width + offsetBox)),
                    y: y
                }
            }, true);
        } else {
            
            callout.lines.setAttributes({
                path: ["M", cur[0], cur[1], "L", x, y, "Z"]
            }, true);
            
            callout.box.setAttributes({
                x: boxx,
                y: boxy,
                width: boxw,
                height: boxh
            }, true);
            
            callout.label.setAttributes({
                x: x + (normal[0] > 0? offsetBox : -(bbox.width + offsetBox)),
                y: y
            }, true);
        }
        for (p in callout) {
            callout[p].show(true);
        }
    },

    
    onAnimate: function(sprite, attr) {
        sprite.show();
        return this.callParent(arguments);
    },

    isItemInPoint: function(x, y, item) {
        var point,
            tolerance = 10,
            abs = Math.abs;

        function dist(point) {
            var dx = abs(point[0] - x),
                dy = abs(point[1] - y);
            return Math.sqrt(dx * dx + dy * dy);
        }
        point = item.point;
        return (point[0] - tolerance <= x && point[0] + tolerance >= x &&
            point[1] - tolerance <= y && point[1] + tolerance >= y);
    }
});



Ext.define('Ext.layout.container.Table', {

    

    alias: ['layout.table'],
    extend:  Ext.layout.container.Container ,
    alternateClassName: 'Ext.layout.TableLayout',

    

    

    
    monitorResize:false,

    type: 'table',
    
    createsInnerCt: true,

    targetCls: Ext.baseCSSPrefix + 'table-layout-ct',
    tableCls: Ext.baseCSSPrefix + 'table-layout',
    cellCls: Ext.baseCSSPrefix + 'table-layout-cell',

    
    tableAttrs: null,

    

    

    getItemSizePolicy: function (item) {
        return this.autoSizePolicy;
    },
    
    initHierarchyState: function (hierarchyStateInner) {    
        hierarchyStateInner.inShrinkWrapTable  = true;
    },

    getLayoutItems: function() {
        var me = this,
            result = [],
            items = me.callParent(),
            item,
            len = items.length, i;

        for (i = 0; i < len; i++) {
            item = items[i];
            if (!item.hidden) {
                result.push(item);
            }
        }
        return result;
    },
    
    getHiddenItems: function(){
        var result = [],
            items = this.owner.items.items,
            len = items.length,
            i = 0, item;
            
        for (; i < len; ++i) {
            item = items[i];
            if (item.rendered && item.hidden) {
                result.push(item);
            }
        }    
        return result;
    },

    
    renderChildren: function() {
        var me = this,
            items = me.getLayoutItems(),
            tbody = me.owner.getTargetEl().child('table', true).tBodies[0],
            rows = tbody.rows,
            i = 0,
            len = items.length,
            hiddenItems = me.getHiddenItems(),
            cells, curCell, rowIdx, cellIdx, item, trEl, tdEl, itemCt, el;

        
        cells = me.calculateCells(items);

        
        
        
        for (; i < len; i++) {
            curCell = cells[i];
            rowIdx = curCell.rowIdx;
            cellIdx = curCell.cellIdx;
            item = items[i];

            
            trEl = rows[rowIdx];
            if (!trEl) {
                trEl = tbody.insertRow(rowIdx);
                if (me.trAttrs) {
                    trEl.set(me.trAttrs);
                }
            }

            
            itemCt = tdEl = Ext.get(trEl.cells[cellIdx] || trEl.insertCell(cellIdx));
            if (me.needsDivWrap()) { 
                itemCt = tdEl.first() || tdEl.createChild({ tag: 'div', role: 'presentation' });
                itemCt.setWidth(null);
            }

            
            if (!item.rendered) {
                me.renderItem(item, itemCt, 0);
            } else if (!me.isValidParent(item, itemCt, rowIdx, cellIdx, tbody)) {
                me.moveItem(item, itemCt, 0);
            }

            
            if (me.tdAttrs) {
                tdEl.set(me.tdAttrs);
            }
            if (item.tdAttrs) {
                tdEl.set(item.tdAttrs);
            }
            tdEl.set({
                colSpan: item.colspan || 1,
                rowSpan: item.rowspan || 1,
                id: item.cellId || '',
                cls: me.cellCls + ' ' + (item.cellCls || '')
            });

            
            if (!cells[i + 1] || cells[i + 1].rowIdx !== rowIdx) {
                cellIdx++;
                while (trEl.cells[cellIdx]) {
                    trEl.deleteCell(cellIdx);
                }
            }
        }

        
        rowIdx++;
        while (tbody.rows[rowIdx]) {
            tbody.deleteRow(rowIdx);
        }
        
        
        
        for (i = 0, len = hiddenItems.length; i < len; ++i) {
            me.ensureInDocument(hiddenItems[i].getEl());
        }
    },
    
    ensureInDocument: function(el){
        var dom = el.dom.parentNode;
        while (dom) {
            if (dom.tagName.toUpperCase() == 'BODY') {
                return;
            }
            dom = dom.parentNode;
        } 
        
        Ext.getDetachedBody().appendChild(el);
    },

    calculate: function (ownerContext) {
        if (!ownerContext.hasDomProp('containerChildrenSizeDone')) {
            this.done = false;
        } else {
            var targetContext = ownerContext.targetContext,
                widthShrinkWrap = ownerContext.widthModel.shrinkWrap,
                heightShrinkWrap = ownerContext.heightModel.shrinkWrap,
                shrinkWrap = heightShrinkWrap || widthShrinkWrap,
                table = shrinkWrap && targetContext.el.child('table', true),
                targetPadding = shrinkWrap && targetContext.getPaddingInfo();

            if (widthShrinkWrap) {
                ownerContext.setContentWidth(table.offsetWidth + targetPadding.width, true);
            }

            if (heightShrinkWrap) {
                ownerContext.setContentHeight(table.offsetHeight + targetPadding.height, true);
            }
        }
    },

    finalizeLayout: function() {
        if (this.needsDivWrap()) {
            
            var items = this.getLayoutItems(),
                i,
                iLen  = items.length,
                item;

            for (i = 0; i < iLen; i++) {
                item = items[i];

                Ext.fly(item.el.dom.parentNode).setWidth(item.getWidth());
            }
        }
        if (Ext.isIE6 || Ext.isIEQuirks) {
            
            this.owner.getTargetEl().child('table').repaint();
        }
    },

    
    calculateCells: function(items) {
        var cells = [],
            rowIdx = 0,
            colIdx = 0,
            cellIdx = 0,
            totalCols = this.columns || Infinity,
            rowspans = [], 
            i = 0, j,
            len = items.length,
            item;

        for (; i < len; i++) {
            item = items[i];

            
            while (colIdx >= totalCols || rowspans[colIdx] > 0) {
                if (colIdx >= totalCols) {
                    
                    colIdx = 0;
                    cellIdx = 0;
                    rowIdx++;

                    
                    for (j = 0; j < totalCols; j++) {
                        if (rowspans[j] > 0) {
                            rowspans[j]--;
                        }
                    }
                } else {
                    colIdx++;
                }
            }

            
            cells.push({
                rowIdx: rowIdx,
                cellIdx: cellIdx
            });

            
            for (j = item.colspan || 1; j; --j) {
                rowspans[colIdx] = item.rowspan || 1;
                ++colIdx;
            }
            ++cellIdx;
        }

        return cells;
    },

    getRenderTree: function() {
        var me = this,
            items = me.getLayoutItems(),
            cells,
            rows = [],
            result = Ext.apply({
                tag: 'table',
                role: 'presentation',
                cls: me.tableCls,
                cellspacing: 0,
                cellpadding: 0,
                cn: {
                    tag: 'tbody',
                    role: 'presentation',
                    cn: rows
                }
            }, me.tableAttrs),
            tdAttrs = me.tdAttrs,
            needsDivWrap = me.needsDivWrap(),
            i, len = items.length, item, curCell, tr, rowIdx, cellIdx, cell;

        
        cells = me.calculateCells(items);

        for (i = 0; i < len; i++) {
            item = items[i];
            
            curCell = cells[i];
            rowIdx = curCell.rowIdx;
            cellIdx = curCell.cellIdx;

            
            tr = rows[rowIdx];
            if (!tr) {
                tr = rows[rowIdx] = {
                    tag: 'tr',
                    role: 'presentation',
                    cn: []
                };
                if (me.trAttrs) {
                    Ext.apply(tr, me.trAttrs);
                }
            }

            
            cell = tr.cn[cellIdx] = {
                tag: 'td',
                role: 'presentation'
            };
            if (tdAttrs) {
                Ext.apply(cell, tdAttrs);
            }
            Ext.apply(cell, {
                colSpan: item.colspan || 1,
                rowSpan: item.rowspan || 1,
                id: item.cellId || '',
                cls: me.cellCls + ' ' + (item.cellCls || '')
            });

            if (needsDivWrap) { 
                cell = cell.cn = {
                    tag: 'div',
                    role: 'presentation'
                };
            }

            me.configureItem(item);
            
            cell.cn = item.getRenderTree();
        }
        return result;
    },

    isValidParent: function(item, target, rowIdx, cellIdx) {
        var tbody,
            correctCell,
            table;

        
        if (arguments.length === 3) {
            table = item.el.up('table');
            return table && table.dom.parentNode === target.dom;
        }
        tbody = this.owner.getTargetEl().child('table', true).tBodies[0];
        correctCell = tbody.rows[rowIdx].cells[cellIdx];
        return item.el.dom.parentNode === correctCell;
    },

    
    needsDivWrap: function() {
        return Ext.isOpera10_5;
    }
});


Ext.define('Ext.container.ButtonGroup', {
    extend:  Ext.panel.Panel ,
    alias: 'widget.buttongroup',
    alternateClassName: 'Ext.ButtonGroup',

                                             

    

    
    baseCls: Ext.baseCSSPrefix + 'btn-group',

    
    layout: {
        type: 'table'
    },

    defaultType: 'button',

    
    frame: true,

    

    frameHeader: false,

    titleAlign: 'center',

    noTitleCls: 'notitle',
    
    ariaRole: 'group',

    initComponent : function() {
        
        var me = this,
            cols = me.columns;

        if (cols) {
            me.layout = Ext.apply({}, {columns: cols}, me.layout);
        }

        if (!me.title) {
            me.addClsWithUI(me.noTitleCls);
        }
        me.callParent(arguments);
    },

    
    onBeforeAdd: function(component) {
        if (component.isButton) {
            if (this.defaultButtonUI && component.ui === 'default' &&
                !component.hasOwnProperty('ui')) {
                component.ui = this.defaultButtonUI;
            } else {
                component.ui = component.ui + '-toolbar';
            }
        }
        this.callParent(arguments);
    },

    
    applyDefaults: function(c) {
        if (!Ext.isString(c)) {
            c = this.callParent(arguments);
        }
        return c;
    }

    
    
    
    
    
});


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


Ext.define('Ext.container.Viewport', {
    extend:  Ext.container.Container ,
    alias: 'widget.viewport',
                                   
    alternateClassName: 'Ext.Viewport',

    
    
    

    

    

    

    

    

    
    isViewport: true,

    ariaRole: 'application',
    
    preserveElOnDestroy: true,
    
    viewportCls: Ext.baseCSSPrefix + 'viewport',

    initComponent : function() {
        var me = this,
            html = document.body.parentNode,
            el = me.el = Ext.getBody();

        
        Ext.getScrollbarSize();
        
        
        me.width = me.height = undefined;

        me.callParent(arguments);
        Ext.fly(html).addCls(me.viewportCls);
        if (me.autoScroll) {
            Ext.fly(html).setStyle(me.getOverflowStyle());
            delete me.autoScroll;
        }
        el.setHeight = el.setWidth = Ext.emptyFn;
        el.dom.scroll = 'no';
        me.allowDomMove = false;
        me.renderTo = me.el;
    },
    
    
    applyTargetCls: function(targetCls) {
        this.el.addCls(targetCls);
    },
    
    onRender: function() {
        var me = this;

        me.callParent(arguments);

        
        
        me.width = Ext.Element.getViewportWidth();
        me.height = Ext.Element.getViewportHeight();
    },

    afterFirstLayout: function() {
        var me = this;

        me.callParent(arguments);
        setTimeout(function() {
            Ext.EventManager.onWindowResize(me.fireResize, me);
        }, 1);
    },

    fireResize : function(width, height){
        
        
        if (width != this.width || height != this.height) {
            this.setSize(width, height);
        }
    },

    initHierarchyState: function(hierarchyState) {
        this.callParent([this.hierarchyState = Ext.rootHierarchyState]);
    },
    
    beforeDestroy: function(){
        var me = this;
        
        me.removeUIFromElement();
        me.el.removeCls(me.baseCls);
        Ext.fly(document.body.parentNode).removeCls(me.viewportCls);
        me.callParent();
    }
});


Ext.define('Ext.data.IdGenerator', {

    
    isGenerator: true,

    
    constructor: function(config) {
        var me = this;

        Ext.apply(me, config);

        if (me.id) {
            Ext.data.IdGenerator.all[me.id] = me;
        }
    },

    

    getRecId: function (rec) {
        return rec.modelName + '-' + rec.internalId;
    },

    

    statics: {
        
        all: {},

        
        get: function (config) {
            var generator,
                id,
                type;

            if (typeof config == 'string') {
                id = type = config;
                config = null;
            } else if (config.isGenerator) {
                return config;
            } else {
                id = config.id || config.type;
                type = config.type;
            }

            generator = this.all[id];
            if (!generator) {
                generator = Ext.create('idgen.' + type, config);
            }

            return generator;
        }
    }
});


Ext.define('Ext.data.SortTypes', {
    
    singleton: true,
    
    
    none : Ext.identityFn,

    
    stripTagsRE : /<\/?[^>]+>/gi,

    
    asText : function(s) {
        return String(s).replace(this.stripTagsRE, "");
    },

    
    asUCText : function(s) {
        return String(s).toUpperCase().replace(this.stripTagsRE, "");
    },

    
    asUCString : function(s) {
        return String(s).toUpperCase();
    },

    
    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(/,/g, ""));
        return isNaN(val) ? 0 : val;
    },

    
    asInt : function(s) {
        var val = parseInt(String(s).replace(/,/g, ""), 10);
        return isNaN(val) ? 0 : val;
    }
});


Ext.define('Ext.data.Types', {
    singleton: true 
                                    
}, function() {
    var st = Ext.data.SortTypes;

    Ext.apply(Ext.data.Types, {
        
        stripRe: /[\$,%]/g,

        
        AUTO: {
            sortType: st.none,
            type: 'auto'
        },

        
        STRING: {
            convert: function(v) {
                var defaultValue = this.useNull ? null : '';
                return (v === undefined || v === null) ? defaultValue : String(v);
            },
            sortType: st.asUCString,
            type: 'string'
        },

        
        INT: {
            convert: function(v) {
                
                
                
                if (typeof v == 'number') {
                    return parseInt(v);
                }
                return v !== undefined && v !== null && v !== '' ?
                    parseInt(String(v).replace(Ext.data.Types.stripRe, ''), 10) : (this.useNull ? null : 0);
            },
            sortType: st.none,
            type: 'int'
        },

        
        FLOAT: {
            convert: function(v) {
                if (typeof v === 'number') {
                    return v;
                }
                return v !== undefined && v !== null && v !== '' ?
                    parseFloat(String(v).replace(Ext.data.Types.stripRe, ''), 10) : (this.useNull ? null : 0);
            },
            sortType: st.none,
            type: 'float'
        },

        
        BOOL: {
            convert: function(v) {
                if (typeof v === 'boolean') {
                    return v;
                }
                if (this.useNull && (v === undefined || v === null || v === '')) {
                    return null;
                }
                return v === 'true' || v == 1;
            },
            sortType: st.none,
            type: 'bool'
        },

        
        DATE: {
            convert: function(v) {
                var df = this.dateReadFormat || this.dateFormat,
                    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: st.asDate,
            type: 'date'
        }
    });

    Ext.apply(Ext.data.Types, {
        
        BOOLEAN: this.BOOL,

        
        INTEGER: this.INT,

        
        NUMBER: this.FLOAT
    });
});


Ext.define('Ext.data.Field', {
                                                       
    alias: 'data.field',

    isField: true,
    
    constructor : function(config) {
        var me = this,
            types = Ext.data.Types,
            st;
        
        if (Ext.isString(config)) {
            config = {name: config};
        }
        Ext.apply(me, config);

        st = me.sortType;

        if (me.type) {
            if (Ext.isString(me.type)) {
                me.type = types[me.type.toUpperCase()] || types.AUTO;
            }
        } else {
            me.type = types.AUTO;
        }

        
        if (Ext.isString(st)) {
            me.sortType = Ext.data.SortTypes[st];
        } else if(Ext.isEmpty(st)) {
            me.sortType = me.type.sortType;
        }

        
        if (!config.hasOwnProperty('convert')) {
            me.convert = me.type.convert; 
        } else if (!me.convert && me.type.convert && !config.hasOwnProperty('defaultValue')) {
            
            
            me.defaultValue = me.type.convert(me.defaultValue);
        }

        if (config.convert) {
            me.hasCustomConvert = true;
        }
    },
    
    
    
    

    

    

    
    dateFormat: null,
    
    
    dateReadFormat: null,
    
    
    dateWriteFormat: null,
    
    
    useNull: false,
    
    
    defaultValue: "",

    
    mapping: null,

    
    sortType : null,

    
    sortDir : "ASC",

    
    allowBlank : true,

    
    persist: true
});


Ext.define('Ext.data.Errors', {
    extend:  Ext.util.MixedCollection ,

    
    isValid: function() {
        return this.length === 0;
    },

    
    getByField: function(fieldName) {
        var errors = [],
            error, i;

        for (i = 0; i < this.length; i++) {
            error = this.items[i];

            if (error.field == fieldName) {
                errors.push(error);
            }
        }

        return errors;
    }
});


Ext.define('Ext.data.validations', {
    singleton: true,
    
    
    presenceMessage: 'must be present',
    
    
    lengthMessage: 'is the wrong length',
    
    
    formatMessage: 'is the wrong format',
    
    
    inclusionMessage: 'is not included in the list of acceptable values',
    
    
    exclusionMessage: 'is not an acceptable value',
    
    
    emailMessage: 'is not a valid email address',
    
    
    emailRe: /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,
    
    
    presence: function(config, value) {
        
        if (arguments.length === 1) {
            value = config;
        }
        
        
        return !!value || value === 0 || value === false;
    },
    
    
    length: function(config, value) {
        if (value === undefined || value === null) {
            return false;
        }
        
        var length = value.length,
            min    = config.min,
            max    = config.max;
        
        if ((min && length < min) || (max && length > max)) {
            return false;
        } else {
            return true;
        }
    },
    
    
    email: function(config, email) {
        return Ext.data.validations.emailRe.test(email);
    },
    
    
    format: function(config, value) {
        return !!(config.matcher && config.matcher.test(value));
    },
    
    
    inclusion: function(config, value) {
        return config.list && Ext.Array.indexOf(config.list,value) != -1;
    },
    
    
    exclusion: function(config, value) {
        return config.list && Ext.Array.indexOf(config.list,value) == -1;
    }
});


Ext.define('Ext.data.Model', {
    alternateClassName: 'Ext.data.Record',

    mixins: {
        observable:  Ext.util.Observable 
    },

               
                           
                               
                         
                          
                             
                               
                                  
      

    compareConvertFields: function(f1, f2) {
        var f1SpecialConvert = f1.convert && f1.type && f1.convert !== f1.type.convert,
            f2SpecialConvert = f2.convert && f2.type && f2.convert !== f2.type.convert;

        if (f1SpecialConvert && !f2SpecialConvert) {
            return 1;
        }

        if (!f1SpecialConvert && f2SpecialConvert) {
            return -1;
        }
        return 0;
    },

    itemNameFn: function(item) {
        return item.name;
    },

    onClassExtended: function(cls, data, hooks) {
        var onBeforeClassCreated = hooks.onBeforeCreated;

        hooks.onBeforeCreated = function(cls, data) {
            var me = this,
                name = Ext.getClassName(cls),
                prototype = cls.prototype,
                superCls = cls.prototype.superclass,

                validations = data.validations || [],
                fields = data.fields || [],
                field,
                associationsConfigs = data.associations || [],
                addAssociations = function(items, type) {
                    var i = 0,
                        len,
                        item;

                    if (items) {
                        items = Ext.Array.from(items);

                        for (len = items.length; i < len; ++i) {
                            item = items[i];

                            if (!Ext.isObject(item)) {
                                item = {model: item};
                            }

                            item.type = type;
                            associationsConfigs.push(item);
                        }
                    }
                },
                idgen = data.idgen,

                fieldsMixedCollection = new Ext.util.MixedCollection(false, prototype.itemNameFn),

                associationsMixedCollection = new Ext.util.MixedCollection(false, prototype.itemNameFn),

                superValidations = superCls.validations,
                superFields = superCls.fields,
                superAssociations = superCls.associations,

                associationConfig, i, ln,
                dependencies = [],

                
                idProperty = 'idProperty' in data ? data.idProperty : prototype.idProperty,

                
                idField = idProperty ? (idProperty.isField ? idProperty : new Ext.data.Field(idProperty)) : null,

                
                idFieldDefined = false,

                
                onFieldAddReplace = function(arg0, arg1, arg2) {
                    var newField,
                        pos;

                    if (fieldsMixedCollection.events.add.firing) {
                        
                        pos = arg0;
                        newField  = arg1;
                    } else {
                        
                        newField = arg2;
                        pos = arg1.originalIndex;
                    }

                    
                    
                    newField.originalIndex = pos;

                    
                    
                    
                    if (idField && ((newField.mapping && (newField.mapping === idField.mapping)) || (newField.name === idField.name))) {
                        prototype.idField = newField;
                        idFieldDefined = true;
                        newField.defaultValue = undefined;
                    }
                },

                
                clsProxy = data.proxy,

                
                fieldConvertSortFn = function() {
                    fieldsMixedCollection.sortBy(prototype.compareConvertFields);
                };

            
            cls.modelName = name;
            prototype.modelName = name;

            
            if (superValidations) {
                validations = superValidations.concat(validations);
            }

            data.validations = validations;

            
            if (superFields) {
                fields = superFields.items.concat(fields);
            }

            fieldsMixedCollection.on({
                add:     onFieldAddReplace,
                replace: onFieldAddReplace
            });  

            for (i = 0, ln = fields.length; i < ln; ++i) {
                field = fields[i];
                fieldsMixedCollection.add(field.isField ? field : new Ext.data.Field(field));
            }

            
            
            
            if (idField && !idFieldDefined) {
                prototype.idField = idField;
                idField.defaultValue = undefined;
                fieldsMixedCollection.add(idField);
            }

            
            fieldConvertSortFn();
            fieldsMixedCollection.on({
                add:     fieldConvertSortFn,
                replace: fieldConvertSortFn
            });

            data.fields = fieldsMixedCollection;

            if (idgen) {
                data.idgen = Ext.data.IdGenerator.get(idgen);
            }

            
            
            addAssociations(data.belongsTo, 'belongsTo');
            delete data.belongsTo;
            addAssociations(data.hasMany, 'hasMany');
            delete data.hasMany;
            addAssociations(data.hasOne, 'hasOne');
            delete data.hasOne;

            if (superAssociations) {
                associationsConfigs = superAssociations.items.concat(associationsConfigs);
            }

            for (i = 0, ln = associationsConfigs.length; i < ln; ++i) {
                dependencies.push('association.' + associationsConfigs[i].type.toLowerCase());
            }

            
            if (clsProxy) {
                if (!clsProxy.isProxy) {
                    dependencies.push('proxy.' + (clsProxy.type || clsProxy));
                }
            }
            
            
            else if (!cls.prototype.proxy) {
                cls.prototype.proxy = cls.prototype.defaultProxyType;
                dependencies.push('proxy.' + cls.prototype.defaultProxyType);
            }

            Ext.require(dependencies, function() {
                Ext.ModelManager.registerType(name, cls);

                for (i = 0, ln = associationsConfigs.length; i < ln; ++i) {
                    associationConfig = associationsConfigs[i];
                    if (associationConfig.isAssociation) {
                        associationConfig = Ext.applyIf({
                            ownerModel: name,
                            associatedModel: associationConfig.model
                        }, associationConfig.initialConfig);
                    } else {
                        Ext.apply(associationConfig, {
                            ownerModel: name,
                            associatedModel: associationConfig.model
                        });
                    }

                    if (Ext.ModelManager.getModel(associationConfig.model) === undefined) {
                        Ext.ModelManager.registerDeferredAssociation(associationConfig);
                    } else {
                        associationsMixedCollection.add(Ext.data.association.Association.create(associationConfig));
                    }
                }

                data.associations = associationsMixedCollection;

                
                
                
                
                
                onBeforeClassCreated.call(me, cls, data, hooks);

                
                if (clsProxy && clsProxy.isProxy) {
                    cls.setProxy(clsProxy);
                }

                
                Ext.ModelManager.onModelDefined(cls);
            });
        };
    },

    inheritableStatics: {
        
        setProxy: function(proxy) {
            
            if (!proxy.isProxy) {
                if (typeof proxy == "string") {
                    proxy = {
                        type: proxy
                    };
                }
                proxy = Ext.createByAlias("proxy." + proxy.type, proxy);
            }
            proxy.setModel(this);
            this.proxy = this.prototype.proxy = proxy;

            return proxy;
        },

        
        getProxy: function() {

            var proxy = this.proxy;

            
            if (!proxy) {
                proxy = this.prototype.proxy;

                
                if (proxy.isProxy) {
                    proxy = proxy.clone()
                }

                return this.setProxy(proxy);
            }

            return proxy;
        },

        
        setFields: function(fields, idProperty, clientIdProperty) {
            var me = this,
                newField,
                idField,
                idFieldDefined = false,
                proto = me.prototype,
                prototypeFields = proto.fields,
                superFields = proto.superclass.fields,
                len,
                i;

            if (idProperty) {
                proto.idProperty = idProperty;
                idField = idProperty.isField ? idProperty : new Ext.data.Field(idProperty);

            }
            if (clientIdProperty) {
                proto.clientIdProperty = clientIdProperty;
            }

            if (prototypeFields) {
                prototypeFields.clear();
            }
            else {
                prototypeFields = me.prototype.fields = new Ext.util.MixedCollection(false, function(field) {
                    return field.name;
                });
            }

            
            if (superFields) {
                fields = superFields.items.concat(fields);
            }

            for (i = 0, len = fields.length; i < len; i++) {
                newField = new Ext.data.Field(fields[i]);

                
                
                
                if (idField && ((newField.mapping && (newField.mapping === idField.mapping)) || (newField.name === idField.name))) {
                    idFieldDefined = true;
                    newField.defaultValue = undefined;
                }
                prototypeFields.add(newField);
            }

            
            
            
            if (idField && !idFieldDefined) {
                idField.defaultValue = undefined;
                prototypeFields.add(idField);
            }

            me.fields = prototypeFields;

            return prototypeFields;
        },

        
        getFields: function() {
            return this.prototype.fields.items;
        },

        
        load: function(id, config) {
            config = Ext.apply({}, config);
            config = Ext.applyIf(config, {
                action: 'read',
                id    : id
            });

            var operation  = new Ext.data.Operation(config),
                scope      = config.scope || this,
                callback;

            callback = function(operation) {
                var record = null,
                    success = operation.wasSuccessful();
                
                if (success) {
                    record = operation.getRecords()[0];
                    
                    if (!record.hasId()) {
                        record.setId(id);
                    }
                    Ext.callback(config.success, scope, [record, operation]);
                } else {
                    Ext.callback(config.failure, scope, [record, operation]);
                }
                Ext.callback(config.callback, scope, [record, operation, success]);
            };

            this.getProxy().read(operation, callback, this);
        }
    },

    statics: {
        
        PREFIX : 'ext-record',
        
        AUTO_ID: 1,
        
        EDIT   : 'edit',
        
        REJECT : 'reject',
        
        COMMIT : 'commit',

        
        id: function(rec) {
            var id = [this.PREFIX, '-', this.AUTO_ID++].join('');
            rec.phantom = true;
            rec.internalId = id;
            return id;
        }
    },

    
    idgen: {
        isGenerator: true,
        type: 'default',

        generate: function () {
            return null;
        },
        getRecId: function (rec) {
            return rec.modelName + '-' + rec.internalId;
        }
    },

    
    editing : false,

    
    dirty : false,

    
    persistenceProperty: 'data',

    evented: false,

    
    isModel: true,

    
    phantom : false,

    
    idProperty: 'id',

    
    clientIdProperty: null,

    
    defaultProxyType: 'ajax',

    
    emptyData: [],

    
    
    

    

    
    
    
    
    

    

    
    constructor: function(data, id, raw, convertedData) {
        
        
        
        
        
        
        

        var me = this,
            passedId = (id || id === 0),
            hasId,
            fields,
            length,
            field,
            name,
            value,
            newId,
            persistenceProperty,
            idProperty = me.idProperty,
            idField = me.idField,
            i;

        
        me.raw = raw || data; 

        
        me.modified = {};

        persistenceProperty = me[me.persistenceProperty] = convertedData || {};

        
        me.data = me[me.persistenceProperty];

        me.mixins.observable.constructor.call(me);

        if (!convertedData) {

            if (data) {
                
                if (!passedId && idProperty) {
                    id = data[idProperty];
                    hasId = (id || id === 0);
                }
            }
            
            else {
                data = me.emptyData;
            } 

            
            fields = me.fields.items;
            length = fields.length;
            i = 0;

            if (Ext.isArray(data)) {
                for (; i < length; i++) {
                    field = fields[i];
                    name  = field.name;

                    
                    
                    value = data[field.originalIndex];

                    if (value === undefined) {
                        value = field.defaultValue;
                    }
                    
                    
                    if (field.convert) {
                        value = field.convert(value, me);
                    }
                    
                    if (value !== undefined) {
                        persistenceProperty[name] = value;
                    }
                }

            } else {
               for (; i < length; i++) {
                    field = fields[i];
                    name  = field.name;
                    value = data[name];
                    if (value === undefined) {
                        value = field.defaultValue;
                    }
                    if (field.convert) {
                        value = field.convert(value, me);
                    }
                    
                    if (value !== undefined) {
                        persistenceProperty[name] = value;
                    }
               }
            }
        }

        
        me.stores = [];

        
        
        if (passedId) {
            hasId = true;
            persistenceProperty[idProperty] = idField && idField.convert ? idField.convert(id) : id;
        }

        
        else if (!hasId) {
            
            newId = me.idgen.generate();
            if (newId != null) {
                me.preventInternalUpdate = true;
                me.setId(newId);
                delete me.preventInternalUpdate;
            }
        }

        
        me.internalId = hasId ? id : Ext.data.Model.id(me);
        

        if (typeof me.init == 'function') {
            me.init();
        }

        
        me.id = me.idgen.getRecId(me);
    },

    
    get: function(field) {
        return this[this.persistenceProperty][field];
    },

    
    
    
    _singleProp: {},

    
    set: function (fieldName, newValue) {
        var me = this,
            data = me[me.persistenceProperty],
            fields = me.fields,
            modified = me.modified,
            single = (typeof fieldName == 'string'),
            currentValue, field, idChanged, key, modifiedFieldNames, name, oldId,
            newId, value, values;

        if (single) {
            values = me._singleProp;
            values[fieldName] = newValue;
        } else {
            values = fieldName;
        }

        for (name in values) {
            if (values.hasOwnProperty(name)) {
                value = values[name];

                if (fields && (field = fields.get(name)) && field.convert) {
                    value = field.convert(value, me);
                }

                currentValue = data[name];
                if (me.isEqual(currentValue, value)) {
                    continue; 
                }

                data[name] = value;
                (modifiedFieldNames || (modifiedFieldNames = [])).push(name);

                if (field && field.persist) {
                    if (modified.hasOwnProperty(name)) {
                        if (me.isEqual(modified[name], value)) {
                            
                            
                            delete modified[name];

                            
                            
                            
                            me.dirty = false;
                            for (key in modified) {
                                if (modified.hasOwnProperty(key)){
                                    me.dirty = true;
                                    break;
                                }
                            }
                        }
                    } else {
                        me.dirty = true;
                        modified[name] = currentValue;
                    }
                }

                if (name == me.idProperty) {
                    idChanged = true;
                    oldId = currentValue;
                    newId = value;
                }
            }
        }

        if (single) {
            
            
            delete values[fieldName];
        }

        if (idChanged) {
            me.changeId(oldId, newId);
        }

        if (!me.editing && modifiedFieldNames) {
            me.afterEdit(modifiedFieldNames);
        }

        return modifiedFieldNames || null;
    },

    
    copyFrom: function(sourceRecord) {
        var me = this,
            fields = me.fields.items,
            fieldCount = fields.length,
            modifiedFieldNames = [],
            field, i = 0,
            myData,
            sourceData,
            idProperty = me.idProperty,
            name,
            value;

        if (sourceRecord) {
            myData = me[me.persistenceProperty];
            sourceData = sourceRecord[sourceRecord.persistenceProperty];
            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;
    },

    
    isEqual: function(a, b) {
        
        if (a instanceof Date && b instanceof Date) {
            return a.getTime() === b.getTime();
        }
        return a === b;
    },

    
    beginEdit : function(){
        var me = this,
            key,
            data,
            o;

        if (!me.editing) {
            me.editing = true;
            me.dirtySave = me.dirty;

            o = me[me.persistenceProperty];
            data = me.dataSave = {};
            for (key in o) {
                if (o.hasOwnProperty(key)) {
                    data[key] = o[key];
                }
            }

            o = me.modified;
            data = me.modifiedSave = {}; 
            for (key in o) {
                if (o.hasOwnProperty(key)) {
                    data[key] = o[key];
                }
            }
        }
    },

    
    cancelEdit : function(){
        var me = this;
        if (me.editing) {
            me.editing = false;
            
            me.modified = me.modifiedSave;
            me[me.persistenceProperty] = me.dataSave;
            me.dirty = me.dirtySave;
            me.modifiedSave = me.dataSave = me.dirtySave = null;
        }
    },

    
    endEdit : function(silent, modifiedFieldNames){
        var me = this,
            dataSave,
            changed;

        silent = silent === true;
        if (me.editing) {
            me.editing = false;
            dataSave = me.dataSave;
            me.modifiedSave = me.dataSave = me.dirtySave = null;
            if (!silent) {
                if (!modifiedFieldNames) {
                    modifiedFieldNames = me.getModifiedFieldNames(dataSave);
                }
                changed = me.dirty || modifiedFieldNames.length > 0;
                if (changed) {
                    me.afterEdit(modifiedFieldNames);
                }
            }
        }
    },

    
    getModifiedFieldNames: function(saved){
        var me = this,
            data = me[me.persistenceProperty],
            modified = [],
            key;

        saved = saved || me.dataSave;
        for (key in data) {
            if (data.hasOwnProperty(key)) {
                if (!me.isEqual(data[key], saved[key])) {
                    modified.push(key);
                }
            }
        }
        return modified; 
    },

    
    getChanges : function(){
        var modified = this.modified,
            changes  = {},
            field;

        for (field in modified) {
            if (modified.hasOwnProperty(field)){
                changes[field] = this.get(field);
            }
        }

        return changes;
    },

    
    isModified : function(fieldName) {
        return this.modified.hasOwnProperty(fieldName);
    },

    
    setDirty : function() {
        var me     = this,
            fields = me.fields.items,
            fLen   = fields.length,
            field, name, f;

        me.dirty = true;

        for (f = 0; f < fLen; f++) {
            field = fields[f];

            if (field.persist) {
                name  = field.name;
                me.modified[name] = me.get(name);
            }
        }
    },


    
    reject : function(silent) {
        var me = this,
            modified = me.modified,
            field;

        for (field in modified) {
            if (modified.hasOwnProperty(field)) {
                if (typeof modified[field] != "function") {
                    me[me.persistenceProperty][field] = modified[field];
                }
            }
        }

        me.dirty = false;
        me.editing = false;
        me.modified = {};

        if (silent !== true) {
            me.afterReject();
        }
    },

    
    commit : function(silent, modifiedFieldNames) {
        var me = this;

        me.phantom = me.dirty = me.editing = false;
        me.modified = {};

        if (silent !== true) {
            me.afterCommit(modifiedFieldNames);
        }
    },

    
    copy : function(newId) {
        var me = this;
        return new me.self(me.raw, newId, null, Ext.apply({}, me[me.persistenceProperty]));
    },

    
    setProxy: function(proxy) {
        
        if (!proxy.isProxy) {
            if (typeof proxy === "string") {
                proxy = {
                    type: proxy
                };
            }
            proxy = Ext.createByAlias("proxy." + proxy.type, proxy);
        }
        proxy.setModel(this.self);
        this.proxy = proxy;

        return proxy;
    },

    
    getProxy: function() {
        return this.hasOwnProperty('proxy') ? this.proxy : this.self.getProxy();
    },

    
    validate: function() {
        var errors      = new Ext.data.Errors(),
            validations = this.validations,
            validators  = Ext.data.validations,
            length, validation, field, valid, type, i;

        if (validations) {
            length = validations.length;

            for (i = 0; i < length; i++) {
                validation = validations[i];
                field = validation.field || validation.name;
                type  = validation.type;
                valid = validators[type](validation, this.get(field));

                if (!valid) {
                    errors.add({
                        field  : field,
                        message: validation.message || validators[type + 'Message']
                    });
                }
            }
        }

        return errors;
    },

    
    isValid: function(){
        return this.validate().isValid();
    },

    
    save: function(options) {
        options = Ext.apply({}, options);

        var me     = this,
            action = me.phantom ? 'create' : 'update',
            scope  = options.scope || me,
            stores = me.stores,
            i = 0,
            storeCount,
            store,
            operation,
            callback;

        Ext.apply(options, {
            records: [me],
            action : action
        });

        operation = new Ext.data.Operation(options);

        callback = function(operation) {
            var success = operation.wasSuccessful();
            
            if (success) {
                for(storeCount = stores.length; i < storeCount; i++) {
                    store = stores[i];
                    store.fireEvent('write', store, operation);
                    store.fireEvent('datachanged', store);
                    
                }
                Ext.callback(options.success, scope, [me, operation]);
            }
            else {
                Ext.callback(options.failure, scope, [me, operation]);
            }

            Ext.callback(options.callback, scope, [me, operation, success]);
        };

        me.getProxy()[action](operation, callback, me);

        return me;
    },

    
    destroy: function(options) {
        options = Ext.apply({
            records: [this],
            action : 'destroy'
        }, options);

        var me = this,
            isNotPhantom = me.phantom !== true,
            scope  = options.scope || me,
            stores,
            i = 0,
            storeCount,
            store,
            args,
            operation,
            callback;

        operation = new Ext.data.Operation(options);

        callback = function(operation) {
            args = [me, operation];

            
            stores = Ext.Array.clone(me.stores);
            if (operation.wasSuccessful()) {
                for (storeCount = stores.length; i < storeCount; i++) {
                    store = stores[i];

                    
                    if (store.remove) {
                        store.remove(me, true);
                    } else {
                        store.fireEvent('bulkremove', store, [me], [store.indexOf(me)], false);    
                    }
                    
                    if (isNotPhantom) {
                        store.fireEvent('write', store, operation);
                    }
                }
                me.clearListeners();
                Ext.callback(options.success, scope, args);
            } else {
                Ext.callback(options.failure, scope, args);
            }
            Ext.callback(options.callback, scope, args);
        };

        
        
        if (isNotPhantom) {
            me.getProxy().destroy(operation, callback, me);
        }
        
        else {
            operation.complete = operation.success = true;
            operation.resultSet = me.getProxy().reader.nullResultSet;
            callback(operation);
        }
        return me;
    },

    
    getId: function() {
        return this.get(this.idField.name);
    },

    
    getObservableId: function() {
        return this.id;
    },

    
    setId: function(id) {
        this.set(this.idProperty, id);
    },

    changeId: function(oldId, newId) {
        var me = this,
            hasOldId, hasId, oldInternalId;

        if (!me.preventInternalUpdate) { 
            hasOldId = me.hasId(oldId);
            hasId = me.hasId(newId);
            oldInternalId = me.internalId;
            me.phantom  = !hasId;
            
            
            
            
            if (hasId !== hasOldId || (hasId && hasOldId)) {
                me.internalId = hasId ? newId : Ext.data.Model.id(me);
            }

            me.fireEvent('idchanged', me, oldId, newId, oldInternalId);
            me.callStore('onIdChanged', oldId, newId, oldInternalId);
         }
    },

    
    hasId: function(id) {
        if (arguments.length === 0) {
            id = this.getId();
        }
        return !!(id || id === 0);
    },

    
    join : function(store) {
        var me = this;

        
        if (!me.stores.length) {
            me.stores[0] = store;
        } else {
            Ext.Array.include(this.stores, store);
        }

        
        this.store = this.stores[0]; 
    },

    
    unjoin: function(store) {
        Ext.Array.remove(this.stores, store);
        this.store = this.stores[0] || null; 
    },

    
    afterEdit : function(modifiedFieldNames) {
        this.callStore('afterEdit', modifiedFieldNames);
    },

    
    afterReject : function() {
        this.callStore('afterReject');
    },

    
    afterCommit: function(modifiedFieldNames) {
        this.callStore('afterCommit', modifiedFieldNames);
    },

    
    callStore: function(fn) {
        var args = Ext.Array.clone(arguments),
            stores = this.stores,
            i = 0,
            len = stores.length,
            store;

        args[0] = this;
        for (; i < len; ++i) {
            store = stores[i];
            if (store && Ext.isFunction(store[fn])) {
                store[fn].apply(store, args);
            }
        }
    },

    
    getData: function(includeAssociated){
        var me     = this,
            fields = me.fields.items,
            fLen   = fields.length,
            data   = {},
            name, f;

        for (f = 0; f < fLen; f++) {
            name = fields[f].name;
            data[name] = me.get(name);
        }

        if (includeAssociated === true) {
            Ext.apply(data, me.getAssociatedData());
        }
        return data;
    },

    
    getAssociatedData: function(){
        return this.prepareAssociatedData({}, 1);
    },

    
    prepareAssociatedData: function(seenKeys, depth) {
        
        var me               = this,
            associations     = me.associations.items,
            associationCount = associations.length,
            associationData  = {},
            
            
            
            toRead           = [],
            toReadKey        = [],
            toReadIndex      = [],
            associatedStore, associatedRecords, associatedRecord, o, index, result, seenDepth,
            associationId, associatedRecordCount, association, i, j, type, name;

        for (i = 0; i < associationCount; i++) {
            association = associations[i];
            associationId = association.associationId;

            seenDepth = seenKeys[associationId];
            if (seenDepth && seenDepth !== depth) {
                continue;
            }
            seenKeys[associationId] = depth;

            type = association.type;
            name = association.name;
            if (type == 'hasMany') {
                
                associatedStore = me[association.storeName];

                
                associationData[name] = [];

                
                if (associatedStore && associatedStore.getCount() > 0) {
                    associatedRecords = associatedStore.data.items;
                    associatedRecordCount = associatedRecords.length;

                    
                    
                    for (j = 0; j < associatedRecordCount; j++) {
                        associatedRecord = associatedRecords[j];
                        associationData[name][j] = associatedRecord.getData();
                        toRead.push(associatedRecord);
                        toReadKey.push(name);
                        toReadIndex.push(j);
                    }
                }
            } else if (type == 'belongsTo' || type == 'hasOne') {
                associatedRecord = me[association.instanceName];
                
                if (associatedRecord !== undefined) {
                    associationData[name] = associatedRecord.getData();
                    toRead.push(associatedRecord);
                    toReadKey.push(name);
                    toReadIndex.push(-1);
                }
            }
        }

        for (i = 0, associatedRecordCount = toRead.length; i < associatedRecordCount; ++i) {
            associatedRecord = toRead[i];
            o = associationData[toReadKey[i]];
            index = toReadIndex[i];
            result = associatedRecord.prepareAssociatedData(seenKeys, depth + 1);
            if (index === -1) {
                Ext.apply(o, result);
            } else {
                Ext.apply(o[index], result);
            }
        }

        return associationData;
    }
});


Ext.define('Ext.data.proxy.Server', {
    extend:  Ext.data.proxy.Proxy ,
    alias : 'proxy.server',
    alternateClassName: 'Ext.data.ServerProxy',
                                 

    

    
    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,

    

    constructor: function(config) {
        var me = this;

        config = config || {};
        
        me.callParent([config]);

        
        me.extraParams = config.extraParams || {};

        me.api = Ext.apply({}, config.api || me.api);
        

        
        me.nocache = me.noCache;
    },

    
    create: function() {
        return this.doRequest.apply(this, arguments);
    },

    read: function() {
        return this.doRequest.apply(this, arguments);
    },

    update: function() {
        return this.doRequest.apply(this, arguments);
    },

    destroy: function() {
        return this.doRequest.apply(this, arguments);
    },

    
    setExtraParam: function(name, value) {
        this.extraParams[name] = value;
    },

    
    buildRequest: function(operation) {
        var me = this,
            
            params = operation.params = Ext.apply({}, operation.params, me.extraParams),
            request;

        
        Ext.applyIf(params, me.getParams(operation));

        
        
        
        if (operation.id !== undefined && params[me.idParam] === undefined) {
            params[me.idParam] = operation.id;
        }

        request = new Ext.data.Request({
            params   : params,
            action   : operation.action,
            records  : operation.records,
            operation: operation,
            url      : operation.url,

            
            
            proxy: me
        });

        request.url = me.buildUrl(request);

        
        operation.request = request;

        return request;
    },

    
    processResponse: function(success, operation, request, response, callback, scope) {
        var me = this,
            reader,
            result;

        if (success === true) {
            reader = me.getReader();

            
            
            
            reader.applyDefaults = operation.action === 'read';

            result = reader.read(me.extractResponseData(response));

            if (result.success !== false) {
                
                Ext.apply(operation, {
                    response: response,
                    resultSet: result
                });

                operation.commitRecords(result.records);
                operation.setCompleted();
                operation.setSuccessful();
            } else {
                operation.setException(result.message);
                me.fireEvent('exception', this, response, operation);
            }
        } else {
            me.setException(operation, response);
            me.fireEvent('exception', this, response, operation);
        }

        
        if (typeof callback == 'function') {
            callback.call(scope || me, operation);
        }

        me.afterRequest(request, success);
    },

    
    setException: function(operation, response) {
        operation.setException({
            status: response.status,
            statusText: response.statusText
        });
    },

    
    extractResponseData: Ext.identityFn,

    
    applyEncoding: function(value) {
        return Ext.encode(value);
    },

    
    encodeSorters: function(sorters) {
        var min = [],
            length = sorters.length,
            i = 0;

        for (; i < length; i++) {
            min[i] = {
                property : sorters[i].property,
                direction: sorters[i].direction
            };
        }
        return this.applyEncoding(min);

    },

    
    encodeFilters: function(filters) {
        var min = [],
            length = filters.length,
            i = 0;

        for (; i < length; i++) {
            min[i] = {
                property: filters[i].property,
                value   : filters[i].value
            };
        }
        return this.applyEncoding(min);
    },

    
    getParams: function(operation) {
        var me = this,
            params = {},
            isDef = Ext.isDefined,
            groupers = operation.groupers,
            sorters = operation.sorters,
            filters = operation.filters,
            page = operation.page,
            start = operation.start,
            limit = operation.limit,
            simpleSortMode = me.simpleSortMode,
            simpleGroupMode = me.simpleGroupMode,
            pageParam = me.pageParam,
            startParam = me.startParam,
            limitParam = me.limitParam,
            groupParam = me.groupParam,
            groupDirectionParam = me.groupDirectionParam,
            sortParam = me.sortParam,
            filterParam = me.filterParam,
            directionParam = me.directionParam,
            hasGroups, index;

        if (pageParam && isDef(page)) {
            params[pageParam] = page;
        }

        if (startParam && isDef(start)) {
            params[startParam] = start;
        }

        if (limitParam && isDef(limit)) {
            params[limitParam] = limit;
        }

        hasGroups = groupParam && groupers && groupers.length > 0;
        if (hasGroups) {
            
            if (simpleGroupMode) {
                params[groupParam] = groupers[0].property;
                params[groupDirectionParam] = groupers[0].direction || 'ASC';
            } else {
                params[groupParam] = me.encodeSorters(groupers);
            }
        }

        if (sortParam && sorters && sorters.length > 0) {
            if (simpleSortMode) {
                index = 0;
                
                if (sorters.length > 1 && hasGroups) {
                    index = 1;
                }
                params[sortParam] = sorters[index].property;
                params[directionParam] = sorters[index].direction;
            } 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 (me.noCache) {
            url = Ext.urlAppend(url, Ext.String.format("{0}={1}", me.cacheString, Ext.Date.now()));
        }

        return url;
    },

    
    getUrl: function(request) {
        return request.url || this.api[request.action] || this.url;
    },

    
    doRequest: function(operation, callback, scope) {
    },

    
    afterRequest: Ext.emptyFn,

    onDestroy: function() {
        Ext.destroy(this.reader, this.writer);
    }
});


Ext.define('Ext.data.proxy.Ajax', {
                           
    extend:  Ext.data.proxy.Server ,
    alias: 'proxy.ajax',
    alternateClassName: ['Ext.data.HttpProxy', 'Ext.data.AjaxProxy'],
    
    
    actionMethods: {
        create : 'POST',
        read   : 'GET',
        update : 'POST',
        destroy: 'POST'
    },
    
    
    
    
    
    defaultActionMethods: {
        create : 'POST',
        read   : 'GET',
        update : 'POST',
        destroy: 'POST'    
    },

    
    binary: false,
    
    
    
    
    paramsAsJson: false,
    
    doRequest: function(operation, callback, scope) {
        var me = this,
            writer  = me.getWriter(),
            request = me.buildRequest(operation),
            method  = me.getMethod(request);
            
        if (operation.allowWrite()) {
            request = writer.write(request);
        }
        
        Ext.apply(request, {
            binary        : me.binary,
            headers       : me.headers,
            timeout       : me.timeout,
            scope         : me,
            callback      : me.createRequestCallback(request, operation, callback, scope),
            method        : method,
            disableCaching: false 
        });
        
        if (method.toUpperCase() !== 'GET' && me.paramsAsJson) {
            
            
            
            request = Ext.apply({
                jsonData: request.params
            }, request);
            delete request.params;
        }
        
        Ext.Ajax.request(request);
        
        return request;
    },
    
    
    getMethod: function(request) {
        var actions = this.actionMethods,
            action = request.action,
            method;
            
        if (actions) {
            method = actions[action];
        }
        return method || this.defaultActionMethods[action];
    },
    
    
    createRequestCallback: function(request, operation, callback, scope) {
        var me = this;
        
        return function(options, success, response) {
            me.processResponse(success, operation, request, response, callback, scope);
        };
    }
});


Ext.define('Ext.data.proxy.Client', {
    extend:  Ext.data.proxy.Proxy ,
    alternateClassName: 'Ext.data.ClientProxy',
    
    
    isSynchronous: true,

    
    clear: function() {
    }
});


Ext.define('Ext.data.proxy.Memory', {
    extend:  Ext.data.proxy.Client ,
    alias: 'proxy.memory',
    alternateClassName: 'Ext.data.MemoryProxy',

    

    

    constructor: function(config) {
        this.callParent([config]);

        
        this.setReader(this.reader);
    },
    
    
    updateOperation: function(operation, callback, scope) {
        var i = 0,
            recs = operation.getRecords(),
            len = recs.length;
            
        for (i; i < len; i++) {
            recs[i].commit();
        }
        operation.setCompleted();
        operation.setSuccessful();
        
        Ext.callback(callback, scope || this, [operation]);
    },
    
    
    create: function() {
        this.updateOperation.apply(this, arguments);
    },
    
    
    update: function() {
        this.updateOperation.apply(this, arguments);
    },
    
    
    destroy: function() {
        this.updateOperation.apply(this, arguments);
    },

    
    read: function(operation, callback, scope) {
        var me = this,
            resultSet = operation.resultSet = me.getReader().read(me.data),
            records = resultSet.records,
            sorters = operation.sorters,
            groupers = operation.groupers,
            filters = operation.filters;

        operation.setCompleted();

        
        if (resultSet.success) {

            
            if (filters && filters.length) {
                records = resultSet.records = Ext.Array.filter(records, Ext.util.Filter.createFilterFn(filters));
                resultSet.total = records.length;
            }

            
            if (groupers && groupers.length) {
                
                sorters = sorters ? sorters.concat(groupers) : sorters;
            }

            
            if (sorters && sorters.length) {
                resultSet.records = Ext.Array.sort(records, Ext.util.Sortable.createComparator(sorters));
            }

            
            
            if (me.enablePaging && operation.start !== undefined && operation.limit !== undefined) {

                
                if (operation.start >= resultSet.total) {
                    resultSet.success = false;
                    resultSet.count = 0;
                    resultSet.records = [];
                }
                
                else {
                    resultSet.records = Ext.Array.slice(resultSet.records, operation.start, operation.start + operation.limit);
                    resultSet.count = resultSet.records.length;
                }
            }
        }

        if (resultSet.success) {
            operation.setSuccessful();
        } else {
            me.fireEvent('exception', me, null, operation);
        }
        Ext.callback(callback, scope || me, [operation]);
    },

    clear: Ext.emptyFn
});


Ext.define('Ext.util.LruCache', {
    extend:  Ext.util.HashMap ,

    

    constructor: function(config) {
        Ext.apply(this, config);
        this.callParent([config]);
    },

    
    add: function(key, newValue) {
        var me = this,
            existingKey = me.findKey(newValue),
            entry;

        
        if (existingKey) {
            me.unlinkEntry(entry = me.map[existingKey]);
            entry.prev = me.last;
            entry.next = null;
        }
        
        else {
            entry = {
                prev: me.last,
                next: null,
                key: key,
                value: newValue
            };
        }

        
        if (me.last) {
            me.last.next = entry;
        }
        
        else {
            me.first = entry;
        }
        me.last = entry;
        me.callParent([key, entry]);
        me.prune();
        return newValue;
    },

    
    insertBefore: function(key, newValue, sibling) {
        var me = this,
            existingKey,
            entry;

        
        
        if (sibling = this.map[this.findKey(sibling)]) {
            existingKey = me.findKey(newValue);

            
            if (existingKey) {
                me.unlinkEntry(entry = me.map[existingKey]);
            }
            
            else {
                entry = {
                    prev: sibling.prev,
                    next: sibling,
                    key: key,
                    value: newValue
                };
            }

            if (sibling.prev) {
                entry.prev.next = entry;
            } else {
                me.first = entry;
            }
            entry.next = sibling;
            sibling.prev = entry;
            me.prune();
            return newValue;
        }
        
        else {
            return me.add(key, newValue);
        }
    },

    
    get: function(key) {
        var entry = this.map[key];
        if (entry) {

            
            if (entry.next) {
                this.moveToEnd(entry);
            }
            return entry.value;
        }
    },

    
    removeAtKey: function(key) {
        this.unlinkEntry(this.map[key]);
        return this.callParent(arguments);
    },

    
    clear: function( initial) {
        this.first = this.last = null;
        return this.callParent(arguments);
    },

    
    unlinkEntry: function(entry) {
        
        if (entry) {
            if (entry.next) {
                entry.next.prev = entry.prev;
            } else {
                this.last = entry.prev;
            }
            if (entry.prev) {
                entry.prev.next = entry.next;
            } else {
                this.first = entry.next;
            }
            entry.prev = entry.next = null;
        }
    },

    
    moveToEnd: function(entry) {
        this.unlinkEntry(entry);

        
        
        if (entry.prev = this.last) {
            this.last.next = entry;
        }
        
        else {
            this.first = entry;
        }
        this.last = entry;
    },

    
    getArray: function(isKey) {
        var arr = [],
            entry = this.first;

        while (entry) {
            arr.push(isKey ? entry.key: entry.value);
            entry = entry.next;
        }
        return arr;
    },

    
    each: function(fn, scope, reverse) {
        var me = this,
            entry = reverse ? me.last : me.first,
            length = me.length;

        scope = scope || me;
        while (entry) {
            if (fn.call(scope, entry.key, entry.value, length) === false) {
                break;
            }
            entry = reverse ? entry.prev : entry.next;
        }
        return me;
    },

    
    findKey: function(value) {
        var key,
            map = this.map;

        for (key in map) {
            
            
            if (map.hasOwnProperty(key) && map[key].value === value) {
                return key;
            }
        }
        return undefined;
    },

    
    clone: function() {
        var newCache = new this.self(this.initialConfig),
            map = this.map,
            key;

        newCache.suspendEvents();
        for (key in map) {
            if (map.hasOwnProperty(key)) {
                newCache.add(key, map[key].value);
            }
        }
        newCache.resumeEvents();
        return newCache;
    },

    
    prune: function() {
        var me = this,
            purgeCount = me.maxSize ? (me.length - me.maxSize) : 0;

        if (purgeCount > 0) {
            for (; me.first && purgeCount; purgeCount--) {
                me.removeAtKey(me.first.key);
            }
        }
    }

  
  
  
  
});


Ext.define('Ext.data.PageMap', {
    extend:  Ext.util.LruCache ,

    
    clear: function(initial) {
        var me = this;
        me.pageMapGeneration = (me.pageMapGeneration || 0) + 1;
        me.callParent(arguments);
    },

    forEach: function(fn, scope) {
        var me = this,
            pageNumbers = Ext.Object.getKeys(me.map),
            pageCount = pageNumbers.length,
            i, j,
            pageNumber,
            page,
            pageSize;

        for (i = 0; i < pageCount; i++) {
            pageNumbers[i] = Number(pageNumbers[i]);
        }
        Ext.Array.sort(pageNumbers);
        scope = scope || me;
        for (i = 0; i < pageCount; i++) {
            pageNumber = pageNumbers[i];
            page = me.getPage(pageNumber);
            pageSize = page.length;
            for (j = 0; j < pageSize; j++) {
                if (fn.call(scope, page[j], (pageNumber - 1) * me.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;
    },

    getPageFromRecordIndex: function() {
        return Ext.data.Store.prototype.getPageFromRecordIndex.apply(this, arguments);
    },

    addAll: function(records) {
        this.addPage(1, records);
    },

    addPage: function(pageNumber, records) {
        var me = this,
            lastPage = pageNumber + Math.floor((records.length - 1) / me.pageSize),
            startIdx,
            page;

        
        
        for (startIdx = 0; pageNumber <= lastPage; pageNumber++, startIdx += me.pageSize) {
            page = Ext.Array.slice(records, startIdx, startIdx + me.pageSize);
            me.add(pageNumber, page);
            me.fireEvent('pageAdded', pageNumber, page);
        }
    },

    getCount: function() {
        var result = this.callParent();
        if (result) {
            result = (result - 1) * this.pageSize + this.last.value.length;
        }
        return result;
    },

    indexOf: function(record) {
        return record ? record.index : -1;
    },

    insert: function() {
    },

    remove: function() {
    },

    removeAt: function() {
    },

    getPage: function(pageNumber) {
        return this.get(pageNumber);
    },

    hasRange: function(start, end) {
        var pageNumber = this.getPageFromRecordIndex(start),
            endPageNumber = this.getPageFromRecordIndex(end);

        for (; pageNumber <= endPageNumber; pageNumber++) {
            if (!this.hasPage(pageNumber)) {
                return false;
            }
        }
        return true;
    },

    hasPage: function(pageNumber) {
        
        return !!this.get(pageNumber);
    },

    getAt: function(index) {
        return this.getRange(index, index)[0];
    },

    getRange: function(start, end) {
        if (!this.hasRange(start, end)) {
            Ext.Error.raise('PageMap asked for range which it does not have');
        }
        var me = this,
            startPageNumber = me.getPageFromRecordIndex(start),
            endPageNumber = me.getPageFromRecordIndex(end),
            dataStart = (startPageNumber - 1) * me.pageSize,
            dataEnd = (endPageNumber * me.pageSize) - 1,
            pageNumber = startPageNumber,
            result = [],
            sliceBegin, sliceEnd, doSlice,
            i = 0, len;

        for (; pageNumber <= endPageNumber; pageNumber++) {

            
            if (pageNumber == startPageNumber) {
                sliceBegin = start - dataStart;
                doSlice = true;
            } else {
                sliceBegin = 0;
                doSlice = false;
            }
            if (pageNumber == endPageNumber) {
                sliceEnd = me.pageSize - (dataEnd - end);
                doSlice = true;
            }

            
            if (doSlice) {
                Ext.Array.push(result, Ext.Array.slice(me.getPage(pageNumber), sliceBegin, sliceEnd));
            } else {
                Ext.Array.push(result, me.getPage(pageNumber));
            }
        }

        
        for (len = result.length; i < len; i++) {
            result[i].index = start++;
        }
        return result;
    }
});


Ext.define('Ext.data.Group', {

    extend:  Ext.util.Observable ,

    key: undefined,

    dirty: true,

    constructor: function(){
        this.callParent(arguments);
        this.records = [];    
    },

    contains: function(record){
        return Ext.Array.indexOf(this.records, record) !== -1;
    },

    add: function(records) {
        Ext.Array.push(this.records, records);
        this.dirty = true;  
    },

    remove: function(records) {
        if (!Ext.isArray(records)) {
            records = [records];
        }

        var len = records.length,
            i;

        for (i = 0; i < len; ++i) {
            Ext.Array.remove(this.records, records[i]);
        }
        this.dirty = true;
    },

    isDirty: function(){
        return this.dirty;    
    },

    hasAggregate: function(){
        return !!this.aggregate;
    },

    setDirty: function(){
        this.dirty = true;
    },

    commit: function(){
        this.dirty = false;
    },

    isCollapsed: function(){
        return this.collapsed;    
    },

    getAggregateRecord: function(forceNew){
        var me = this,
            Model;

        if (forceNew === true || me.dirty || !me.aggregate) {
            Model = me.store.model;
            me.aggregate = new Model();
            me.aggregate.isSummary = true;
        }
        return me.aggregate;
    }

});


Ext.define('Ext.data.Store', {
    extend:  Ext.data.AbstractStore ,

    alias: 'store.store',

    
    
    
               
                                
                         
                              
                                
                               
                               
                           
                        
      

           
                           
                          
      

    
    remoteSort: false,

    
    remoteFilter: false,

    
    remoteGroup : false,

    

    

    

    
    groupField: undefined,

    
    groupDir: "ASC",

    
    trailingBufferZone: 25,

    
    leadingBufferZone: 200,

    
    pageSize: undefined,

    
    currentPage: 1,

    
    clearOnPageLoad: true,

    
    loading: false,

    
    sortOnFilter: true,

    
    buffered: false,

    
    purgePageCount: 5,

    
    clearRemovedOnLoad: true,

    defaultPageSize: 25,

    
    defaultViewSize: 100,

    
    addRecordsOptions: {
        addRecords: true
    },

    statics: {
        recordIdFn: function(record) {
            return record.internalId;
        },
        recordIndexFn: function(record) {
            return record.index;
        },
        grouperIdFn: function(grouper) {
            return grouper.id || grouper.property;
        },
        groupIdFn: function(group) {
            return group.key;
        }
    },

    
    constructor: function(config) {
        
        config = Ext.apply({}, config);

        var me = this,
            groupers = config.groupers || me.groupers,
            groupField = config.groupField || me.groupField,
            proxy,
            data;

        
        
        
        
        data = config.data || me.data;

        if (data) {
            me.inlineData = data;
            delete config.data;
        }

        if (!groupers && groupField) {
            groupers = [{
                property : groupField,
                direction: config.groupDir || me.groupDir
            }];
        
            
            if (config.getGroupString || (me.getGroupString !== Ext.data.Store.prototype.getGroupString)) {
                groupers[0].getGroupString = function(record) {
                    return me.getGroupString(record);
                }
            }
        }
        delete config.groupers;

        
        me.groupers = new Ext.util.MixedCollection(false, Ext.data.Store.grouperIdFn);
        me.groupers.addAll(me.decodeGroupers(groupers));

        me.groups = new Ext.util.MixedCollection(false, Ext.data.Store.groupIdFn);

        
        me.callParent([config]);
        

        if (me.buffered) {
            me.data = new Ext.data.PageMap({
                store: me,
                keyFn: Ext.data.Store.recordIdFn,
                pageSize: me.pageSize,
                maxSize: me.purgePageCount,
                listeners: {
                    
                    
                    clear: me.onPageMapClear,
                    scope: me
                }
            });
            me.pageRequests = {};

            
            me.remoteSort = me.remoteGroup = me.remoteFilter = true;

            me.sortOnLoad = false;
            me.filterOnLoad = false;
        } else {
           
            me.data = new Ext.util.MixedCollection({
                getKey: Ext.data.Store.recordIdFn,
                maintainIndices: true
            });
        }

        
        if (me.remoteGroup) {
            me.remoteSort = true;
        }

        
        me.sorters.insert(0, me.groupers.getRange());

        proxy = me.proxy;
        data = me.inlineData;

        
        
        if (!me.buffered && !me.pageSize) {
            me.pageSize = me.defaultPageSize;
        }

        
        if (data) {
            if (proxy instanceof Ext.data.proxy.Memory) {
                proxy.data = data;
                me.read();
            } else {
                me.add.apply(me, [data]);
            }
            
            
            
            if (me.sorters.items.length && !me.remoteSort) {
                me.group(null, null, true);
            }

            delete me.inlineData;
        }
        else if (me.autoLoad) {
            
            Ext.defer(me.load, 1, me, [ typeof me.autoLoad === 'object' ? me.autoLoad : undefined ]);
        }
    },

    onBeforeSort: function() {
        var groupers = this.groupers;
        if (groupers.getCount() > 0) {
            this.sort(groupers.items, 'prepend', false);
        }
    },

    
    decodeGroupers: function(groupers) {
        if (!Ext.isArray(groupers)) {
            if (groupers === undefined) {
                groupers = [];
            } else {
                groupers = [groupers];
            }
        }

        var length = groupers.length,
            Grouper = Ext.util.Grouper,
            config, i, result = [];

        for (i = 0; i < length; i++) {
            config = groupers[i];

            if (!(config instanceof Grouper)) {
                if (Ext.isString(config)) {
                    config = {
                        property: config
                    };
                }

                config = Ext.apply({
                    root     : 'data',
                    direction: "ASC"
                }, config);

                
                if (config.fn) {
                    config.sorterFn = config.fn;
                }

                
                if (typeof config == 'function') {
                    config = {
                        sorterFn: config
                    };
                }

                
                result.push(new Grouper(config));
            } else {
                result.push(config);
            }
        }
        return result;
    },

    
    group: function(groupers, direction,  suppressEvent) {
        var me = this,
            grouper,
            newGroupers;

        
        if (groupers) {

            
            me.sorters.removeAll(me.groupers.items);

            if (Ext.isArray(groupers)) {
                newGroupers = groupers;
            } else if (Ext.isObject(groupers)) {
                newGroupers = [groupers];
            } else if (Ext.isString(groupers)) {
                grouper = me.groupers.get(groupers);

                if (!grouper) {
                    grouper = {
                        property : groupers,
                        direction: direction || 'ASC'
                    };
                    newGroupers = [grouper];
                } else if (direction === undefined) {
                    grouper.toggle();
                } else {
                    grouper.setDirection(direction);
                }
            }

            
            if (newGroupers && newGroupers.length) {
                me.groupers.clear();
                me.groupers.addAll(me.decodeGroupers(newGroupers));
            }

            
            me.sorters.insert(0, me.groupers.items);
        }

        if (me.remoteGroup) {
            if (me.buffered) {
                me.data.clear();
                me.loadPage(1, { groupChange: true });
            } else {
                me.load({
                    scope: me,
                    callback: suppressEvent ? null : me.fireGroupChange
                });
            }
        } else {
            me.doSort(me.generateComparator());
            me.constructGroups();
            if (!suppressEvent) {
                me.fireGroupChange();
            }
        }
    },

    getGroupField: function(){
        var first = this.groupers.first(),
            group;

        if (first) {
            group = first.property;
        }   
        return group; 
    },

    constructGroups: function(){
        var me = this,
            data = this.data.items,
            len = data.length,
            groups = me.groups,
            groupValue, i, group, rec;

        groups.clear();

        if (me.isGrouped()) {
            for (i = 0; i < len; ++i) {
                rec = data[i];
                groupValue = me.getGroupString(rec);
                group = groups.get(groupValue);
                if (!group) {
                    group = new Ext.data.Group({
                        key: groupValue,
                        store: me
                    });
                    groups.add(group);
                }
                group.add(rec);
            }
        }
    },

    
    clearGrouping: function() {
        var me       = this,
            groupers = me.groupers.items,
            gLen     = groupers.length,
            g;

        
        for (g = 0; g < gLen; g++) {
            me.sorters.remove(groupers[g]);
        }
        me.groupers.clear();
        if (me.remoteGroup) {
            if (me.buffered) {
                me.data.clear();
                me.loadPage(1, { groupChange: true });
            } else {
                me.load({
                    scope: me,
                    callback: me.fireGroupChange
                });
            }
        } else {
            me.groups.clear();
            if (me.sorters.length) {
                me.sort();
            } else {
                me.fireEvent('datachanged', me);
                me.fireEvent('refresh', me);
            }
            me.fireGroupChange();
        }
    },

    
    isGrouped: function() {
        return this.groupers.getCount() > 0;
    },

    
    fireGroupChange: function() {
        this.fireEvent('groupchange', this, this.groupers);
    },

    
    getGroups: function(requestGroupString) {
        var records = this.data.items,
            length = records.length,
            groups = [],
            pointers = {},
            record,
            groupStr,
            group,
            i;

        for (i = 0; i < length; i++) {
            record = records[i];
            groupStr = this.getGroupString(record);
            group = pointers[groupStr];

            if (group === undefined) {
                group = {
                    name: groupStr,
                    children: []
                };

                groups.push(group);
                pointers[groupStr] = group;
            }

            group.children.push(record);
        }

        return requestGroupString ? pointers[requestGroupString] : groups;
    },

    
    getGroupsForGrouper: function(records, grouper) {
        var length = records.length,
            groups = [],
            oldValue,
            newValue,
            record,
            group,
            i;

        for (i = 0; i < length; i++) {
            record = records[i];
            newValue = grouper.getGroupString(record);

            if (newValue !== oldValue) {
                group = {
                    name: newValue,
                    grouper: grouper,
                    records: []
                };
                groups.push(group);
            }

            group.records.push(record);

            oldValue = newValue;
        }

        return groups;
    },

    
    getGroupsForGrouperIndex: function(records, grouperIndex) {
        var me = this,
            groupers = me.groupers,
            grouper = groupers.getAt(grouperIndex),
            groups = me.getGroupsForGrouper(records, grouper),
            length = groups.length,
            i;

        if (grouperIndex + 1 < groupers.length) {
            for (i = 0; i < length; i++) {
                groups[i].children = me.getGroupsForGrouperIndex(groups[i].records, grouperIndex + 1);
            }
        }

        for (i = 0; i < length; i++) {
            groups[i].depth = grouperIndex;
        }

        return groups;
    },

    
    getGroupData: function(sort) {
        var me = this;
        if (sort !== false) {
            me.sort();
        }

        return me.getGroupsForGrouperIndex(me.data.items, 0);
    },

    
    getGroupString: function(instance) {
        var group = this.groupers.first();
        if (group) {
            return group.getGroupString(instance);
        }
        return '';
    },

    
    insert: function(index, records) {
        var me = this,
            sync = false,
            i, len, record,
            defaults = me.modelDefaults,
            out;

        
        if (!Ext.isIterable(records)) {
            out = records = [records];
        } else {
            out = [];
        }
        len = records.length;

        if (len) {
            for (i = 0; i < len; i++) {
                record = records[i];
                if (!record.isModel) {
                    record = me.createModel(record);
                }
                out[i] = record;
                if (defaults) {
                    record.set(defaults);
                }

                record.join(me);
                sync = sync || record.phantom === true;
            }
            
            me.data.insert(index, out);

            if (me.snapshot) {
                me.snapshot.addAll(out);
            }

            if (me.requireSort) {
                
                me.suspendEvents();
                me.sort();
                me.resumeEvents();
            }

            if (me.isGrouped()) {
                me.updateGroupsOnAdd(out);
            }

            me.fireEvent('add', me, out, index);
            me.fireEvent('datachanged', me);
            if (me.autoSync && sync && !me.autoSyncSuspended) {
                me.sync();
            }
        }
        return out;
    },

    updateGroupsOnAdd: function(records) {
        var me = this,
            groups = me.groups,
            len = records.length,
            i, groupName, group, rec;

        for (i = 0; i < len; ++i) {
            rec = records[i];
            groupName = me.getGroupString(rec);
            group = groups.getByKey(groupName);
            if (!group) {
                group = groups.add(new Ext.data.Group({
                    key: groupName,
                    store: me
                }));
            }
            group.add(rec);
        }
    },

    updateGroupsOnRemove: function(records) {
        var me = this,
            groups = me.groups,
            len = records.length,
            i, groupName, group, rec;

        for (i = 0; i < len; ++i) {
            rec = records[i];
            groupName = me.getGroupString(rec);
            group = groups.getByKey(groupName);

            if (group) {
                group.remove(rec);
                if (group.records.length === 0) {
                    groups.remove(group);
                }    
            }
        }
    },

    updateGroupsOnUpdate: function(record, modifiedFieldNames){
        var me = this,
            groupField = me.getGroupField(),
            groupName = me.getGroupString(record),
            groups = me.groups,
            len, i, items, group;

        if (modifiedFieldNames && Ext.Array.contains(modifiedFieldNames, groupField)) {

            
            if (me.buffered) {
                Ext.Error.raise({
                    msg: 'Cannot move records between groups in a buffered store record'
                });
            }

            
            items = groups.items;
            for (i = 0, len = items.length; i < len; ++i) {
                group = items[i];
                if (group.contains(record)) {
                    group.remove(record);
                    break;
                }
            }
            group = groups.getByKey(groupName);
            if (!group) {
                group = groups.add(new Ext.data.Group({
                    key: groupName,
                    store: me
                }));
            }
            group.add(record);

            
            
            me.data.remove(record);
            me.data.insert(me.data.findInsertionIndex(record, me.generateComparator()), record);

        } else {
            
            groups.getByKey(groupName).setDirty();    
        }
    },

    
    add: function(arg) {
        var me = this,
            records,
            length, isSorted;


        
        if (Ext.isArray(arg)) {
            records = arg;
        } else {
            records = arguments;
        }

        length = records.length;
        isSorted = !me.remoteSort && me.sorters && me.sorters.items.length;

        
        
        if (isSorted && length === 1) {
            return [ me.addSorted(me.createModel(records[0])) ];
        }

        
        
        if (isSorted) {
            me.requireSort = true;
        }

        records = me.insert(me.data.length, records);
        delete me.requireSort;

        return records;
    },

    
    addSorted: function(record) {
        var me = this,
            index = me.data.findInsertionIndex(record, me.generateComparator());

        me.insert(index, record);
        return record;
    },

    
    createModel: function(record) {
        if (!record.isModel) {
            record = Ext.ModelManager.create(record, this.model);
        }

        return record;
    },

    onUpdate: function(record, type, modifiedFieldNames){
        if (this.isGrouped()) {
            this.updateGroupsOnUpdate(record, modifiedFieldNames);
        }
    },

    
    each: function(fn, scope) {
        var data = this.data.items,
            dLen = data.length,
            record, d;

        for (d = 0; d < dLen; d++) {
            record = data[d];
            if (fn.call(scope || record, record, d, dLen) === false) {
                break;
            }
        }
    },

    
    remove: function(records,  isMove, silent) {
        
        isMove = isMove === true;

        var me = this,
            sync = false,
            snapshot = me.snapshot,
            data = me.data,
            i = 0,
            length,
            info = [],
            allRecords = [],
            indexes = [],
            item,
            isNotPhantom,
            index,
            record,
            removeRange,
            removeCount,
            fireRemoveEvent = !silent && me.hasListeners.remove;

        
        if (records.isModel) {
            records = [records];
            length = 1;
        }

        
        else if (Ext.isIterable(records)) {
            length = records.length;
        }

        
        
        else if (typeof records === 'object') {
            removeRange = true;
            i = records.start;
            length = records.end + 1;
            removeCount = length - i;
        }

        
        
        if (!removeRange) {
            for (i = 0; i < length; ++i) {

                record = records[i];

                
                if (typeof record == 'number') {
                    index = record;
                    record = data.getAt(index);
                }
                
                else {
                    index = me.indexOf(record);
                }

                
                if (record && index > -1) {
                    info.push({
                        record: record,
                        index: index
                    });
                }

                
                if (snapshot) {
                    snapshot.remove(record);
                }
            }

            
            info = Ext.Array.sort(info, function(o1, o2) {
                var index1 = o1.index,
                    index2 = o2.index;

                return index1 === o2.index2 ? 0 : (index1 < index2 ? -1 : 1);
            });

            
            i = 0;
            length = info.length;
        }

        
        
        if (!length) {
            return;
        }

        
        
        
        for (; i < length; i++) {
            if (removeRange) {
                record = data.getAt(i);
                index = i;
            } else {
                item = info[i];
                record = item.record;
                index = item.index;
            }

            allRecords.push(record);
            indexes.push(index);

            isNotPhantom = record.phantom !== true;
            
            if (!isMove && isNotPhantom) {

                
                
                record.removedFrom = index;
                me.removed.push(record);
            }

            record.unjoin(me);

            
            
            index -= i;
            sync = sync || isNotPhantom;

            
            
            if (!removeRange) {
                data.removeAt(index);

                
                if (fireRemoveEvent) {
                    me.fireEvent('remove', me, record, index, !!isMove);
                }
            }
        }

        
        
        if (removeRange) {
            data.removeRange(records.start, removeCount);
        }

        if (!silent) {
            me.fireEvent('bulkremove', me, allRecords, indexes, !!isMove);
            me.fireEvent('datachanged', me);
        }
        if (!isMove && me.autoSync && sync && !me.autoSyncSuspended) {
            me.sync();
        }
    },

    
    removeAt: function(index, count) {
        var me = this,
            storeCount = me.getCount();

        if (index <= storeCount) {
            if (arguments.length === 1) {
                me.remove([ index ]);
            } else if (count) {
                me.remove({
                    start: index,
                    end: Math.min(index + count, storeCount) - 1
                });
            }
        }
    },

    
    removeAll: function(silent) {
        var me = this,
            snapshot = me.snapshot,
            data = me.data;
            
        if (snapshot) {
            snapshot.removeAll(data.getRange());
        }

        if (me.buffered) {
            if (data) {
                if (silent) {
                    me.suspendEvent('clear');
                }
                data.clear();
                if (silent) {
                    me.resumeEvent('clear');
                }
            }            
        }

        else {
            
            
            me.remove({
                start: 0,
                end: me.getCount() - 1
            }, false, silent);
            if (silent !== true) {
                me.fireEvent('clear', me);
            }
        }
    },

    
    load: function(options) {
        var me = this;

        if (typeof options == 'function') {
            options = {
                callback: options
            };
        } else {
            options = Ext.apply({}, options);
        }

        
        if (me.remoteGroup && !options.groupers && me.groupers.items.length) {
            options.groupers = me.groupers.items;
        }
        options.page = options.page || me.currentPage;
        options.start = (options.start !== undefined) ? options.start : (options.page - 1) * me.pageSize;
        options.limit = options.limit || me.pageSize;
        options.addRecords = options.addRecords || false;

        if (me.buffered) {
            options.limit = me.viewSize || me.defaultViewSize;
            
            
            options.loadCallback = options.callback;
            delete options.callback;
            return me.loadToPrefetch(options);
        }
        return me.callParent([options]);
    },

    reload: function(options) {
        var me = this,
            startIdx,
            endIdx,
            startPage,
            endPage,
            i,
            waitForReload,
            bufferZone,
            records;

        if (!options) {
            options = {};
        }

        
        
        if (me.buffered) {

            
            delete me.totalCount;

            
            me.data.clear(true);

            waitForReload = function() {
                if (me.rangeCached(startIdx, endIdx)) {
                    me.loading = false;
                    me.data.un('pageAdded', waitForReload);
                    records = me.data.getRange(startIdx, endIdx);
                    me.fireEvent('load', me, records, true);
                }
            };
            bufferZone = Math.ceil((me.leadingBufferZone + me.trailingBufferZone) / 2);

            
            if (!me.lastRequestStart) {
                startIdx = options.start || 0;
                endIdx = startIdx + (options.count || me.pageSize) - 1;
            } else {
                startIdx = me.lastRequestStart;
                endIdx = me.lastRequestEnd;
            }

            
            startPage = me.getPageFromRecordIndex(Math.max(startIdx - bufferZone, 0));
            endPage = me.getPageFromRecordIndex(endIdx + bufferZone);

            if (me.fireEvent('beforeload', me, options) !== false) {
                me.loading = true;

                
                
                me.data.on('pageAdded', waitForReload);

                
                for (i = startPage; i <= endPage; i++) {
                    me.prefetchPage(i, options);
                }
            }
        } else {
            return me.callParent(arguments);
        }
    },

    
    onProxyLoad: function(operation) {
        var me = this,
            resultSet = operation.getResultSet(),
            records = operation.getRecords(),
            successful = operation.wasSuccessful();

        if (me.isDestroyed) {
            return;
        }
        
        if (resultSet) {
            me.totalCount = resultSet.total;
        }

        
        
        
        me.loading = false;
        if (successful) {
            me.loadRecords(records, operation);
        }

        if (me.hasListeners.load) {
            me.fireEvent('load', me, records, successful);
        }

        
        
        if (me.hasListeners.read) {
            me.fireEvent('read', me, records, successful);
        }

        
        Ext.callback(operation.callback, operation.scope || me, [records, operation, successful]);
    },

    
    getNewRecords: function() {
        return (this.snapshot || this.data).filterBy(this.filterNew).items;
    },

    
    getUpdatedRecords: function() {
        return (this.snapshot || this.data).filterBy(this.filterUpdated).items;
    },

    
    filter: function(filters, value) {
        if (Ext.isString(filters)) {
            filters = {
                property: filters,
                value: value
            };
        }

        var me = this,
            decoded = me.decodeFilters(filters),
            i,
            doLocalSort = me.sorters.length && me.sortOnFilter && !me.remoteSort,
            length = decoded.length;

        
        for (i = 0; i < length; i++) {
            me.filters.replace(decoded[i]);
        }

        filters = me.filters.items;

        
        
        
        if (filters.length) {
            if (me.remoteFilter) {
                
                delete me.totalCount;

                
                
                
                if (me.buffered) {
                    me.data.clear();
                    me.loadPage(1);
                } else {
                    
                    me.currentPage = 1;
                    
                    me.load();
                }
            } else {
                
                me.snapshot = me.snapshot || me.data.clone();

                
                me.data = me.snapshot.filter(filters);

                
                me.constructGroups();

                if (doLocalSort) {
                    me.sort();
                } else {
                    
                    me.fireEvent('datachanged', me);
                    me.fireEvent('refresh', me);
                }
            }
            me.fireEvent('filterchange', me, filters);
        }
    },

    
    clearFilter: function(suppressEvent) {
        var me = this;

        me.filters.clear();

        if (me.remoteFilter) {

            
            if (suppressEvent) {
                return;
            }

            
            delete me.totalCount;

            
            
            
            if (me.buffered) {
                me.data.clear();
                me.loadPage(1);
            } else {
                
                me.currentPage = 1;
                me.load();
            }
        } else if (me.isFiltered()) {
            me.data = me.snapshot;
            delete me.snapshot;

            
            me.constructGroups();

            if (suppressEvent !== true) {
                me.fireEvent('datachanged', me);
                me.fireEvent('refresh', me);
            }
        }
        me.fireEvent('filterchange', me, me.filters.items);
    },

    
    removeFilter: function(toRemove, applyFilters) {
        var me = this;

        if (!me.remoteFilter && me.isFiltered()) {
            if (toRemove instanceof Ext.util.Filter) {
                me.filters.remove(toRemove);
            } else {
                me.filters.removeAtKey(toRemove);
            }

            if (applyFilters !== false) {

                
                if (me.filters.length) {
                    me.filter();
                }

                
                else {
                    me.clearFilter();
                }
            } else {
                me.fireEvent('filterchange', me, me.filters.items);
            }
        }
    },

    
    addFilter: function(filters, applyFilters) {
        var me = this,
            decoded,
            i,
            length;

        
        decoded = me.decodeFilters(filters);
        length = decoded.length;
        for (i = 0; i < length; i++) {
            me.filters.replace(decoded[i]);
        }

        if (applyFilters !== false && me.filters.length) {
            me.filter();
        } else {
            me.fireEvent('filterchange', me, me.filters.items);
        }
    },

    
    isFiltered: function() {
        var snapshot = this.snapshot;
        return !!(snapshot && snapshot !== this.data);
    },

    
    filterBy: function(fn, scope) {
        var me = this;

        me.snapshot = me.snapshot || me.data.clone();
        me.data = me.queryBy(fn, scope || me);
        me.fireEvent('datachanged', me);
        me.fireEvent('refresh', me);
    },

    
    queryBy: function(fn, scope) {
        var me = this;
        return (me.snapshot || me.data).filterBy(fn, scope || me);
    },

    
    query: function(property, value, anyMatch, caseSensitive, exactMatch) {
        var me = this,
            queryFn = me.createFilterFn(property, value, anyMatch, caseSensitive, exactMatch),
            results = me.queryBy(queryFn);

        
        if(!results) {
            results = new Ext.util.MixedCollection();
        }

        return results;
    },

    
    loadData: function(data, append) {
        var length = data.length,
            newData = [],
            i;

        
        for (i = 0; i < length; i++) {
            newData.push(this.createModel(data[i]));
        }

        this.loadRecords(newData, append ? this.addRecordsOptions : undefined);
    },

    
    loadRawData : function(data, append) {
         var me      = this,
             result  = me.proxy.reader.read(data),
             records = result.records;

         if (result.success) {
             me.totalCount = result.total;
             me.loadRecords(records, append ? me.addRecordsOptions : undefined);
         }
     },

    
    loadRecords: function(records, options) {
        var me     = this,
            i      = 0,
            length = records.length,
            start,
            addRecords,
            snapshot = me.snapshot;

        if (options) {
            start = options.start;
            addRecords = options.addRecords;
        }

        if (!addRecords) {
            delete me.snapshot;
            me.clearData(true);
        } else if (snapshot) {
            snapshot.addAll(records);
        }

        me.data.addAll(records);

        if (start !== undefined) {
            for (; i < length; i++) {
                records[i].index = start + i;
                records[i].join(me);
            }
        } else {
            for (; i < length; i++) {
                records[i].join(me);
            }
        }

        
        me.suspendEvents();

        if (me.filterOnLoad && !me.remoteFilter) {
            me.filter();
        }

        if (me.sortOnLoad && !me.remoteSort) {
            me.sort(undefined, undefined, undefined, true);
        }

        me.resumeEvents();
        if (me.isGrouped()) {
            me.constructGroups();
        }
        me.fireEvent('datachanged', me);
        me.fireEvent('refresh', me);
    },

    
    
    loadPage: function(page, options) {
        var me = this;

        me.currentPage = page;

        
        options = Ext.apply({
            page: page,
            start: (page - 1) * me.pageSize,
            limit: me.pageSize,
            addRecords: !me.clearOnPageLoad
        }, options);

        if (me.buffered) {
            options.limit = me.viewSize || me.defaultViewSize;
            options.loadCallback = options.callback;
            delete options.callback;
            return me.loadToPrefetch(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,
            records,
            i;

        
        
        
        
        
        if (!me.buffered && me.data) {
            records = me.data.items;
            i = records.length;
            while (i--) {
                records[i].unjoin(me);
            }
        }

        
        if (me.data) {
            me.data.clear();
        }
        
        if (isLoad !== true || me.clearRemovedOnLoad) {
            me.removed.length = 0;
        }
    },

    loadToPrefetch: function(options) {
        var me = this,
            i,
            records,
            dataSetSize,
            prefetchOptions = options,

            
            startIdx = options.start,
            endIdx = options.start + options.limit - 1,

            
            loadEndIdx = Math.min(endIdx, options.start + (me.viewSize || options.limit) - 1),

            
            
            startPage = me.getPageFromRecordIndex(Math.max(startIdx - me.trailingBufferZone, 0)),
            endPage = me.getPageFromRecordIndex(endIdx + me.leadingBufferZone),

            
            waitForRequestedRange = function() {
                if (me.rangeCached(startIdx, loadEndIdx)) {
                    me.loading = false;
                    records = me.data.getRange(startIdx, loadEndIdx);
                    me.data.un('pageAdded', waitForRequestedRange);

                    
                    if (me.hasListeners.guaranteedrange) {
                        me.guaranteeRange(startIdx, loadEndIdx, options.callback, options.scope);
                    }
                    
                    
                    if (options.loadCallback) {
                        options.loadCallback.call(options.scope || me, records, operation, true);
                    }
                    
                    if (options.callback) {
                        options.callback.call(options.scope||me, records, startIdx, endIdx, options);
                    }
                    me.fireEvent('datachanged', me);
                    me.fireEvent('refresh', me);
                    me.fireEvent('load', me, records, true);
                    if (options.groupChange) {
                        me.fireGroupChange();
                    }
                }
            }, operation;
            

        if (me.fireEvent('beforeload', me, options) !== false) {

            
            delete me.totalCount;

            me.loading = true;

            
            
            if (options.callback) {
                prefetchOptions = Ext.apply({}, options);
                delete prefetchOptions.callback;
            }

            
            
            
            
            me.on('prefetch', function(store, records, successful, op) {
                if (successful) {
                    
                    operation = op;
                    
                    
                    if ((dataSetSize = me.getTotalCount())) {

                        
                        me.data.on('pageAdded', waitForRequestedRange);

                        
                        loadEndIdx = Math.min(loadEndIdx, dataSetSize - 1);

                        
                        endPage = me.getPageFromRecordIndex(Math.min(loadEndIdx + me.leadingBufferZone, dataSetSize - 1));

                        for (i = startPage + 1; i <= endPage; ++i) {
                            me.prefetchPage(i, prefetchOptions);
                        }
                    } else {
                        me.fireEvent('datachanged', me);
                        me.fireEvent('refresh', me);
                        me.fireEvent('load', me, records, true);
                    }
                }
                
                else {
                    me.fireEvent('load', me, records, false);
                }
            }, null, {single: true});

            me.prefetchPage(startPage, prefetchOptions);
        }
    },

    
    
    prefetch: function(options) {
        var me = this,
            pageSize = me.pageSize,
            proxy,
            operation;

        
        if (pageSize) {
            if (me.lastPageSize && pageSize != me.lastPageSize) {
                Ext.Error.raise("pageSize cannot be dynamically altered");
            }
            if (!me.data.pageSize) {
                me.data.pageSize = pageSize;
            }
        }

        
        else {
            me.pageSize = me.data.pageSize = pageSize = options.limit;
        }

        
        me.lastPageSize = pageSize;

        
        if (!options.page) {
            options.page = me.getPageFromRecordIndex(options.start);
            options.start = (options.page - 1) * pageSize;
            options.limit = Math.ceil(options.limit / pageSize) * pageSize;
        }

        
        if (!me.pageRequests[options.page]) {

            
            options = Ext.apply({
                action : 'read',
                filters: me.filters.items,
                sorters: me.sorters.items,
                groupers: me.groupers.items,

                
                
                pageMapGeneration: me.data.pageMapGeneration
            }, options);

            operation = new Ext.data.Operation(options);

            if (me.fireEvent('beforeprefetch', me, operation) !== false) {
                proxy = me.proxy;
                me.pageRequests[options.page] = proxy.read(operation, me.onProxyPrefetch, me);
                if (proxy.isSynchronous) {
                    delete me.pageRequests[options.page];
                }
            }
        }

        return me;
    },

    
    onPageMapClear: function() {
        var me = this,
            loadingFlag = me.wasLoading,
            reqs = me.pageRequests,
            req,
            page;

        
        if (me.data.events.pageadded) {
            me.data.events.pageadded.clearListeners();
        }

        
        
        
        me.loading = true;
        me.totalCount = 0;

        
        for (page in reqs) {
            if (reqs.hasOwnProperty(page)) {
                req = reqs[page];
                delete reqs[page];
                delete req.callback;
            }
        }

        
        me.fireEvent('clear', me);

        
        
        me.loading = loadingFlag;
    },

    
    prefetchPage: function(page, options) {
        var me = this,
            pageSize = me.pageSize || me.defaultPageSize,
            start = (page - 1) * me.pageSize,
            total = me.totalCount;

        
        if (total !== undefined && me.getCount() === total) {
            return;
        }

        
        me.prefetch(Ext.applyIf({
            page     : page,
            start    : start,
            limit    : pageSize
        }, options));
    },

    
    onProxyPrefetch: function(operation) {
        var me = this,
            resultSet = operation.getResultSet(),
            records = operation.getRecords(),
            successful = operation.wasSuccessful(),
            page = operation.page;

        
        
        if (operation.pageMapGeneration === me.data.pageMapGeneration) {

            if (resultSet) {
                me.totalCount = resultSet.total;
                me.fireEvent('totalcountchange', me.totalCount);
            }

            
            if (page !== undefined) {
                delete me.pageRequests[page];
            }

            
            me.loading = false;
            me.fireEvent('prefetch', me, records, successful, operation);

            
            
            if (successful) {
                me.cachePage(records, operation.page);
            }

            
            Ext.callback(operation.callback, operation.scope || 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.data.addPage(page, records);
    },

    
    rangeCached: function(start, end) {
        return this.data && this.data.hasRange(start, end);
    },

    
    pageCached: function(page) {
        return this.data && this.data.hasPage(page);
    },
    
    
    pagePending: function(page) {
        return !!this.pageRequests[page];
    },

    
    rangeSatisfied: function(start, end) {
        return this.rangeCached(start, end);
    },

    
    getPageFromRecordIndex: function(index) {
        return Math.floor(index / this.pageSize) + 1;
    },

    
    onGuaranteedRange: 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);


        range = me.data.getRange(start, end);
        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, options)
     },

    
    prefetchRange: function(start, end) {
        var me = this,
            startPage, endPage, page;
        if (!me.rangeCached(start, end)) {
            startPage = me.getPageFromRecordIndex(start);
            endPage = me.getPageFromRecordIndex(end);

            
            
            
            me.data.maxSize = me.purgePageCount ? (endPage - startPage + 1) + me.purgePageCount : 0;

            
            for (page = startPage; page <= endPage; page++) {
                if (!me.pageCached(page)) {
                    me.prefetchPage(page);
                }
            }
        }
    },

    primeCache: function(start, end, direction) {
        var me = this;

        
        if (direction === -1) {
            start = Math.max(start - me.leadingBufferZone, 0);
            end   = Math.min(end   + me.trailingBufferZone, me.totalCount - 1);
        }
        
        else if (direction === 1) {
            start = Math.max(Math.min(start - me.trailingBufferZone, me.totalCount - me.pageSize), 0);
            end   = Math.min(end + me.leadingBufferZone, me.totalCount - 1);
        }
        
        else {
            start = Math.min(Math.max(Math.floor(start - ((me.leadingBufferZone + me.trailingBufferZone) / 2)), 0), me.totalCount - me.pageSize);
            end =   Math.min(Math.max(Math.ceil (end   + ((me.leadingBufferZone + me.trailingBufferZone) / 2)), 0), me.totalCount - 1);
        }
        me.prefetchRange(start, end);
    },

    
    
    sort: function(sorters) {
        var me = this;

        
        if (sorters && me.buffered && me.remoteSort) {
            me.data.clear();
        }
        return me.callParent(arguments);
    },

    
    
    
    doSort: function(sorterFn) {
        var me = this,
            range,
            ln,
            i;

        if (me.remoteSort) {

            
            
            
            if (me.buffered) {
                me.data.clear();
                me.loadPage(1);
            } else {
                
                me.load();
            }
        } else {
            me.data.sortBy(sorterFn);
            if (!me.buffered) {
                range = me.getRange();
                ln = range.length;
                for (i = 0; i < ln; i++) {
                    range[i].index = i;
                }
            }
            me.fireEvent('datachanged', me);
            me.fireEvent('refresh', me);
        }
    },

    
    find: function(property, value, start, anyMatch, caseSensitive, exactMatch) {
        var fn = this.createFilterFn(property, value, anyMatch, caseSensitive, exactMatch);
        return fn ? this.data.findIndexBy(fn, null, start) : -1;
    },

    
    findRecord: function() {
        var me = this,
            index = me.find.apply(me, arguments);
        return index !== -1 ? me.getAt(index) : null;
    },

    
    createFilterFn: function(property, value, anyMatch, caseSensitive, exactMatch) {
        if (Ext.isEmpty(value)) {
            return false;
        }

        
        value = Ext.util.AbstractMixedCollection.prototype.createValueMatcher(value, anyMatch, caseSensitive, exactMatch);
        return function(r) {
            return value.test(r.data[property]);
        };
    },

    
    findExact: function(property, value, start) {
        return this.data.findIndexBy(function(rec) {
            return rec.isEqual(rec.get(property), value);
        },
        this, start);
    },

    
    findBy: function(fn, scope, start) {
        return this.data.findIndexBy(fn, scope, start);
    },

    
    collect: function(dataIndex, allowNull, bypassFilter) {
        var me = this,
            data = (bypassFilter === true && me.snapshot) ? me.snapshot : me.data;

        return data.collect(dataIndex, 'data', allowNull);
    },

    
    getCount: function() {
        return this.data.getCount();
    },

    
    getTotalCount: function() {
        return this.totalCount || 0;
    },

    
    getAt: function(index) {
        return this.data.getAt(index);
    },

    
    getRange: function(start, end, options) {

        var me = this,
            requiredStart,
            requiredEnd,
            maxIndex = me.totalCount - 1,
            lastRequestStart = me.lastRequestStart,
            pageAddHandler,
            result;

        options = Ext.apply({
            prefetchStart: start,
            prefetchEnd: end
        }, options);

        if (me.buffered) {
            
            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.onGuaranteedRange(options);
                result = me.data.getRange(start, end);
            }
            
            else {
                
                me.fireEvent('cachemiss', me, start, end);

                
                pageAddHandler = function(page, records) {
                    if (me.rangeCached(requiredStart, requiredEnd)) {
                        
                        me.fireEvent('cachefilled', me, start, end);
                        me.data.un('pageAdded', pageAddHandler);
                        me.onGuaranteedRange(options);
                    }
                };
                me.data.on('pageAdded', pageAddHandler);

                
                
                
                me.prefetchRange(start, end);

            }
            
            me.primeCache(start, end, start < lastRequestStart ? -1 : 1);
        } else {
            result = me.data.getRange(start, end);

            
            if (options.callback) {
                options.callback.call(options.scope || me, result, start, end, options)
            }
        }

        return result;
    },

    
    getById: function(id) {
        var result = (this.snapshot || this.data).findBy(function(record) {
            return record.getId() === id;
        });
        return result;
    },

    
    getByInternalId: function(internalId) {
        var result;

        if (this.buffered) {
            result = (this.snapshot || this.data).findBy(function(record) {
                return record.internalId === internalId;
            });
        } else {
            result = this.data.get(internalId);
        }
        return result;
    },

    
    indexOf: function(record) {
        return this.data.indexOf(record);
    },

    
    indexOfTotal: function(record) {
        var index = record.index;
        if (index || index === 0) {
            return index;
        }
        return this.indexOf(record);
    },

    
    indexOfId: function(id) {
        return this.indexOf(this.getById(id));
    },

    

    
    first: function(grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(function(records) {
                return records.length ? records[0] : undefined;
            }, me, true);
        } else {
            return me.data.first();
        }
    },

    
    last: function(grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(function(records) {
                var len = records.length;
                return len ? records[len - 1] : undefined;
            }, me, true);
        } else {
            return me.data.last();
        }
    },

    
    sum: function(field, grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(me.getSum, me, true, [field]);
        } else {
            return me.getSum(me.data.items, field);
        }
    },

    
    getSum: function(records, field) {
        var total = 0,
            i = 0,
            len = records.length;

        for (; i < len; ++i) {
            total += records[i].get(field);
        }

        return total;
    },

    
    count: function(grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(function(records) {
                return records.length;
            }, me, true);
        } else {
            return me.getCount();
        }
    },

    
    min: function(field, grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(me.getMin, me, true, [field]);
        } else {
            return me.getMin(me.data.items, field);
        }
    },

    
    getMin: function(records, field) {
        var i = 1,
            len = records.length,
            value, min;

        if (len > 0) {
            min = records[0].get(field);
        }

        for (; i < len; ++i) {
            value = records[i].get(field);
            if (value < min) {
                min = value;
            }
        }
        return min;
    },

    
    max: function(field, grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(me.getMax, me, true, [field]);
        } else {
            return me.getMax(me.data.items, field);
        }
    },

    
    getMax: function(records, field) {
        var i = 1,
            len = records.length,
            value,
            max;

        if (len > 0) {
            max = records[0].get(field);
        }

        for (; i < len; ++i) {
            value = records[i].get(field);
            if (value > max) {
                max = value;
            }
        }
        return max;
    },

    
    average: function(field, grouped) {
        var me = this;
        if (grouped && me.isGrouped()) {
            return me.aggregate(me.getAverage, me, true, [field]);
        } else {
            return me.getAverage(me.data.items, field);
        }
    },

    
    getAverage: function(records, field) {
        var i = 0,
            len = records.length,
            sum = 0;

        if (records.length > 0) {
            for (; i < len; ++i) {
                sum += records[i].get(field);
            }
            return sum / len;
        }
        return 0;
    },

    
    aggregate: function(fn, scope, grouped, args) {
        args = args || [];
        if (grouped && this.isGrouped()) {
            var groups = this.getGroups(),
                len = groups.length,
                out = {},
                group, i;

            for (i = 0; i < len; ++i) {
                group = groups[i];
                out[group.name] = this.getAggregate(fn, scope || this, group.children, args);
            }
            return out;
        } else {
            return this.getAggregate(fn, scope, this.data.items, args);
        }
    },

    getAggregate: function(fn, scope, records, args){
        args = args || [];
        return fn.apply(scope || this, [records].concat(args));
    },

    onIdChanged: function(rec, oldId, newId, oldInternalId){
        var snapshot = this.snapshot;
        if (snapshot) {
            snapshot.updateKey(oldInternalId, newId);
        }
        this.data.updateKey(oldInternalId, newId);
        this.callParent(arguments);
    },

    
    commitChanges : function(){
        var me = this,
            recs = me.getModifiedRecords(),
            len = recs.length,
            i = 0;

        for (; i < len; i++){
            recs[i].commit();
        }

        
        
        me.removed.length = 0;
    },

    filterNewOnly: function(item){
        return item.phantom === true;
    },

    
    
    
    getRejectRecords: function() {
        
        return Ext.Array.push(this.data.filterBy(this.filterNewOnly).items, this.getUpdatedRecords());
    },

    
    rejectChanges : function() {
        var me = this,
            recs = me.getRejectRecords(),
            len = recs.length,
            i = 0,
            rec;

        for (; i < len; i++) {
            rec = recs[i];
            rec.reject();
            if (rec.phantom) {
                me.remove(rec);
            }
        }

        
        recs = me.removed;
        len = recs.length;
        for (i = len-1; i >= 0; i--) {
            rec = recs[i];
            me.insert(rec.removedFrom || 0, rec);
            rec.reject();
        }

        
        
        me.removed.length = 0;
    }
}, function() {
    
    
    
    Ext.regStore('ext-empty-store', {fields: [], proxy: 'memory'});
});


Ext.define('Ext.data.reader.Array', {
    extend:  Ext.data.reader.Json ,
    alternateClassName: 'Ext.data.ArrayReader',
    alias : 'reader.array',

    
    totalProperty: undefined,
    successProperty: undefined,

    
    createFieldAccessExpression: function(field, fieldVarName, dataName) {
            
            
        var index  = (field.mapping == null) ? field.originalIndex : field.mapping,
            result;

        if (typeof index === 'function') {
            result = fieldVarName + '.mapping(' + dataName + ', this)';
        } else {
            if (isNaN(index)) {
                index = '"' + index + '"';
            }
            result = dataName + "[" + index + "]";
        }
        return result;
    }
});


Ext.define('Ext.data.ArrayStore', {
    extend:  Ext.data.Store ,
    alias: 'store.array',
               
                                
                               
      

    constructor: function(config) {
        config = Ext.apply({
            proxy: {
                type: 'memory',
                reader: 'array'
            }
        }, config);
        this.callParent([config]);
    },

    loadData: function(data, append) {
        if (this.expandData === true) {
            var r = [],
                i = 0,
                ln = data.length;

            for (; i < ln; i++) {
                r[r.length] = [data[i]];
            }

            data = r;
        }

        this.callParent([data, append]);
    }
}, function() {
    
    Ext.data.SimpleStore = Ext.data.ArrayStore;
    
});


Ext.define('Ext.data.Batch', {
    mixins: {
        observable:  Ext.util.Observable 
    },

    
    autoStart: false,
    
    
    pauseOnException: false,

    
    current: -1,

    
    total: 0,

    
    isRunning: false,

    
    isComplete: false,

    
    hasException: false,

    
    constructor: function(config) {
        var me = this;

        

        

        

        me.mixins.observable.constructor.call(me, config);

        
        me.operations = [];
        
        
        me.exceptions = [];
    },

    
    add: function(operation) {
        this.total++;

        operation.setBatch(this);

        this.operations.push(operation);
        
        return this;
    },

    
    start: function( index) {
        var me = this;
        
        if (me.isRunning) {
            return me;
        }
        
        me.exceptions.length = 0;
        me.hasException = false;
        me.isRunning = true;

        return me.runOperation(Ext.isDefined(index) ? index : me.current + 1);
    },
    
    
    retry: function() {
        return this.start(this.current);
    },

    
    runNextOperation: function() {
        return this.runOperation(this.current + 1);
    },

    
    pause: function() {
        this.isRunning = false;
        return this;
    },

    
    runOperation: function(index) {
        var me = this,
            operations = me.operations,
            operation = operations[index],
            onProxyReturn;

        if (operation === undefined) {
            me.isRunning = false;
            me.isComplete = true;
            me.fireEvent('complete', me, operations[operations.length - 1]);
        } else {
            me.current = index;

            onProxyReturn = function(operation) {
                var hasException = operation.hasException();

                if (hasException) {
                    me.hasException = true;
                    me.exceptions.push(operation);
                    me.fireEvent('exception', me, operation);
                }

                if (hasException && me.pauseOnException) {
                    me.pause();
                } else {
                    operation.setCompleted();
                    me.fireEvent('operationcomplete', me, operation);
                    me.runNextOperation();
                }
            };

            operation.setStarted();

            me.proxy[operation.action](operation, onProxyReturn, me);
        }
        
        return me;
    }
});


Ext.define('Ext.data.BufferStore', {
    extend:  Ext.data.Store ,
    alias: 'store.buffer',
    sortOnLoad: false,
    filterOnLoad: false,
    
    constructor: function() {
        Ext.Error.raise('The BufferStore class has been deprecated. Instead, specify the buffered config option on Ext.data.Store');
    }
});



Ext.define('Ext.direct.Manager', {
    singleton: true,

               
                                   
                               
      

    mixins: {
        observable:  Ext.util.Observable 
    },

    
    exceptions: {
        TRANSPORT: 'xhr',
        PARSE: 'parse',
        DATA: 'data',
        LOGIN: 'login',
        SERVER: 'exception'
    },
    
    constructor: function() {
        var me = this;

        me.addEvents(
            
            'event',
            
            
            'exception'
        );
        
        me.transactions = new Ext.util.MixedCollection();
        me.providers    = new Ext.util.MixedCollection();

        me.mixins.observable.constructor.call(me);
    },

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

    
    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) {
        if (Ext.isString(fn)) {
            var parts = fn.split('.'),
                i = 0,
                len = parts.length,
                current = Ext.global;
                
            while (current && i < len) {
                current = current[parts[i]];
                ++i;
            }
            
            fn = Ext.isFunction(current) ? current : null;
        }
        
        return fn || null;
    }
    
}, function() {
    
    Ext.Direct = Ext.direct.Manager;
});


Ext.define('Ext.data.proxy.Direct', {
    

    extend:  Ext.data.proxy.Server ,
    alternateClassName: 'Ext.data.DirectProxy',

    alias: 'proxy.direct',

                                     

    

    

    
    paramOrder: undefined,

    
    paramsAsHash: true,

    
    directFn : undefined,

    

    

    
    paramOrderRe: /[\s,|]/,

    constructor: function(config){
        var me = this,
            paramOrder;
            
        me.callParent(arguments);
        
        paramOrder = me.paramOrder;
        if (Ext.isString(paramOrder)) {
            me.paramOrder = paramOrder.split(me.paramOrderRe);
        }
    },
    
    resolveMethods: function() {
        var me = this,
            fn = me.directFn,
            api = me.api,
            Manager = Ext.direct.Manager,
            method;
        
        if (fn) {
            method = me.directFn = Manager.parseMethod(fn);
            
            if (!Ext.isFunction(method)) {
                Ext.Error.raise('Cannot resolve directFn ' + fn);
            }
        }
        else if (api) {
            for (fn in api) {
                if (api.hasOwnProperty(fn)) {
                    method = api[fn];
                    api[fn] = Manager.parseMethod(method);
                    
                    if (!Ext.isFunction(api[fn])) {
                        Ext.Error.raise('Cannot resolve Direct api ' + fn + ' method ' + method);
                    }
                }
            }
        }
        
        me.methodsResolved = true;
    },

    doRequest: function(operation, callback, scope) {
        var me = this,
            writer = me.getWriter(),
            request = me.buildRequest(operation),
            params = request.params,
            args = [],
            fn, method;
        
        if (!me.methodsResolved) {
            me.resolveMethods();
        }

        fn = me.api[request.action] || me.directFn;
        

        if (operation.allowWrite()) {
            request = writer.write(request);
        }

        if (operation.action == 'read') {
            
            method = fn.directCfg.method;
            args = method.getArgs(params, me.paramOrder, me.paramsAsHash);
        } else {
            args.push(request.jsonData);
        }

        Ext.apply(request, {
            args: args,
            directFn: fn
        });
        args.push(me.createRequestCallback(request, operation, callback, scope), me);
        fn.apply(window, args);
    },

    
    applyEncoding: Ext.identityFn,

    createRequestCallback: function(request, operation, callback, scope){
        var me = this;

        return function(data, event){
            me.processResponse(event.status, operation, request, event, callback, scope);
        };
    },

    
    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');
            Ext.copyTo(proxy.reader, config, 'totalProperty,root,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);


        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 = setTimeout(Ext.bind(me.handleTimeout, me, [request]), timeout);
        }

        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;

        if (request.timeout) {
            clearTimeout(request.timeout);
        }
        delete this[request.callbackName];
        delete this.requests[request.id];
        this.cleanupErrorHandling(request);
        Ext.fly(request.script).remove();

        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]);
        Ext.EventManager.idleEvent.fire();
    },

    
    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'],
                                 

    defaultWriterType: 'base',

    
    callbackKey : 'callback',

    
    recordParam: 'records',

    
    autoAppendParams: true,

    constructor: function() {
        this.addEvents(
            
            'exception'
        );
        this.callParent(arguments);
    },

    
    doRequest: function(operation, callback, scope) {
        
        var me      = this,
            request = me.buildRequest(operation),
            params  = request.params;

        
        Ext.apply(request, {
            callbackKey: me.callbackKey,
            timeout: me.timeout,
            scope: me,
            disableCaching: false, 
            callback: me.createRequestCallback(request, operation, callback, scope)
        });

        
        
        if (me.autoAppendParams) {
            request.params = {};
        }

        request.jsonp = Ext.data.JsonP.request(request);
        
        request.params = params;
        operation.setStarted();
        me.lastRequest = request;

        return request;
    },

    
    createRequestCallback: function(request, operation, callback, scope) {
        var me = this;

        return function(success, response, errorType) {
            delete me.lastRequest;
            me.processResponse(success, operation, request, response, callback, scope);
        };
    },

    
    setException: function(operation, response) {
        operation.setException(operation.request.jsonp.errorType);
    },


    
    buildUrl: function(request) {
        var me      = this,
            url     = me.callParent(arguments),
            records = request.records,
            writer  = me.getWriter(),
            params,
            filters,
            filter, i;

        
        
        if (writer && request.operation.allowWrite()) {
            request = writer.write(request);
        }

        
        params  = request.params;
        filters = params.filters,
        delete params.filters;
        if (filters && filters.length) {
            for (i = 0; i < filters.length; i++) {
                filter = filters[i];

                if (filter.value) {
                    params[filter.property] = filter.value;
                }
            }
        }

        
        if ((!writer || !writer.encode) && Ext.isArray(records) && records.length > 0) {
            params[me.recordParam] = me.encodeRecords(records);
        }

        
        
        if (me.autoAppendParams) {
            url = Ext.urlAppend(url, Ext.Object.toQueryString(params));
        }

        return url;
    },

    
    abort: function() {
        var lastRequest = this.lastRequest;
        if (lastRequest) {
            Ext.data.JsonP.abort(lastRequest.jsonp);
        }
    },

    
    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.NodeInterface', {
               
                         
                              
      

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    


    

    

    

    

    

    

    statics: {
        
        decorate: function(modelClass) {
            var idName, idField, idType;
            
            
            if (typeof modelClass == 'string') {
                modelClass = Ext.ModelManager.getModel(modelClass);
            } else if (modelClass.isModel) {
                modelClass = Ext.ModelManager.getModel(modelClass.modelName);
            }
            
            
            if (modelClass.prototype.isNode) {
                return;
            }

            idName  = modelClass.prototype.idProperty;
            idField = modelClass.prototype.fields.get(idName);
            idType  = modelClass.prototype.fields.get(idName).type.type;

            modelClass.override(this.getPrototypeBody());
            this.applyFields(modelClass, [
                { name : 'parentId',   type : idType,    defaultValue : null,  useNull : idField.useNull                },
                { 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 }
            ]);
        },
        
        applyFields: function(modelClass, addFields) {
            var modelPrototype = modelClass.prototype,
                fields = modelPrototype.fields,
                keys = fields.keys,
                ln = addFields.length,
                addField, i;

            for (i = 0; i < ln; i++) {
                addField = addFields[i];
                if (!Ext.Array.contains(keys, addField.name)) {
                    fields.add(new Ext.data.Field(addField));
                }
            }

        },

        getPrototypeBody: function() {
            var bubbledEvents = {
                idchanged     : true,
                append        : true,
                remove        : true,
                bulkremove    : true,
                move          : true,
                insert        : true,
                beforeappend  : true,
                beforeremove  : true,
                beforemove    : true,
                beforeinsert  : true,
                expand        : true,
                collapse      : true,
                beforeexpand  : true,
                beforecollapse: true,
                sort          : true,
                rootchange    : true
            };
            return {
                
                isNode: true,
                
                constructor: function() {
                    var me = this;
                    me.callParent(arguments);
                    me.firstChild = me.lastChild = me.parentNode = me.previousSibling = me.nextSibling = null;
                    me.childNodes = [];

                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    return me;
                },
                
                createNode: function(node) {
                    if (!node.isModel) {
                        node = Ext.ModelManager.create(node, this.modelName);
                    }
                    
                    
                    
                    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,
                        oldDepth = me.data.depth,
                        childInfo = {},
                        children = me.childNodes,
                        childCount = children.length,
                        i,
                        phantom = me.phantom,
                        dataObject = me[me.persistenceProperty],
                        fields = me.fields,
                        modified = me.modified,
                        propName, newValue,
                        field, currentValue, key,
                        newParentId = info.parentId,
                        settingIndexInNewParent,
                        persistentField;

                    if (!info) {
                        Ext.Error.raise('NodeInterface expects update info to be passed');
                    }

                    
                    
                    
                    for (propName in info) {
                        field = fields.get(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.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
                        };

                    
                    if (Ext.isArray(node)) {
                        
                        me.callStore('suspendAutoSync');
                        for (i = 0, ln = node.length - 1; i < ln; i++) {
                            me.appendChild(node[i], suppressEvents, commit);
                        }
                        
                        me.callStore('resumeAutoSync');
                        me.appendChild(node[ln], suppressEvents, commit);
                    } else {
                        
                        node = me.createNode(node);

                        if (suppressEvents !== true && me.fireEventArgs("beforeappend", [me, node]) === false) {
                            return false;
                        }

                        index = me.childNodes.length;
                        oldParent = node.parentNode;

                        
                        if (oldParent) {
                            if (suppressEvents !== true && node.fireEventArgs("beforemove", [node, oldParent, me, index]) === false) {
                                return false;
                            }
                            oldParent.removeChild(node, false, false, true);
                        }

                        
                        Ext.suspendLayouts();

                        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
                            });
                            previousSibling.triggerUIUpdate();
                        } else {
                            node.previousSibling = null;
                        }

                        
                        childInfo.isFirst = index === 0;
                        childInfo.index = index;
                        node.updateInfo(commit, childInfo);

                        
                        if (!me.isLoaded()) {
                            me.set('loaded', true);
                        } else if (me.childNodes.length === 1) {
                            me.triggerUIUpdate();
                        }

                        
                        if (index && me.childNodes[index - 1].isExpanded()) {
                            me.childNodes[index - 1].cascadeBy(me.triggerUIUpdate);
                        }

                        if(!node.isLeaf() && node.phantom) {
                            node.set('loaded', true);
                        }

                        
                        Ext.resumeLayouts(true);

                        if (suppressEvents !== true) {
                            me.fireEventArgs("append", [me, node, index]);

                            if (oldParent) {
                                node.fireEventArgs("move", [node, oldParent, me, index]);
                            }
                        }

                        return node;
                    }
                },

                
                getOwnerTree: function() {
                    var node = this,
                        store;
                        
                    while (node.parentNode) {
                        node = node.parentNode;
                    }
                    store = node.store;
                    if (store) {
                        if (store.treeStore) {
                            store = store.treeStore;
                        }
                        
                        if (store.tree) {
                            return store.ownerTree;
                        }
                    }
                    return undefined;
                },

                
                removeChild : function(node, destroy, suppressEvents, isMove) {
                    var me = this,
                        index = me.indexOf(node),
                        i, childCount,
                        previousSibling;

                    if (index === -1 || (suppressEvents !== true && me.fireEventArgs("beforeremove", [me, node, !!isMove]) === false)) {
                        return false;
                    }

                    
                    Ext.suspendLayouts();

                    
                    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 (previousSibling.isExpanded()) {
                            previousSibling.cascadeBy(me.triggerUIUpdate);
                        }
                        
                        else {
                            previousSibling.triggerUIUpdate();
                        }
                    }

                    
                    if (!me.childNodes.length) {
                        me.triggerUIUpdate();
                    }

                    
                    Ext.resumeLayouts(true);

                    if (suppressEvents !== true) {
                        
                        node.removeContext = {
                            parentNode: node.parentNode,
                            previousSibling: node.previousSibling,
                            nextSibling: node.nextSibling
                        };

                        node.previousSibling = node.nextSibling = node.parentNode = null;
                        me.fireEventArgs('remove', [me, node, !!isMove]);

                        
                        node.removeContext = null;
                    }

                    
                    
                    if (destroy) {
                        node.destroy(true);
                    } else {
                        node.clear();
                    }

                    return node;
                },

                
                copy: function(newId, deep) {
                    var me = this,
                        result = me.callParent(arguments),
                        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(destroy) {
                    var me = this;

                    
                    me.parentNode = me.previousSibling = me.nextSibling = null;
                    if (destroy) {
                        me.firstChild = me.lastChild = null;
                    }
                },

                
                destroy : function(silent) {
                    
                    var me      = this,
                        options = me.destroyOptions,
                        nodes   = me.childNodes,
                        nLen    = nodes.length,
                        n;

                    if (silent === true) {
                        me.clear(true);

                        for (n = 0; n < nLen; n++) {
                            nodes[n].destroy(true);
                        }

                        me.childNodes = null;
                        delete me.destroyOptions;
                        me.callParent([options]);
                    } else {
                        me.destroyOptions = silent;
                        
                        me.remove(true);
                    }
                },

                
                insertBefore : function(node, refNode, suppressEvents) {
                    var me = this,
                        index     = me.indexOf(refNode),
                        oldParent = node.parentNode,
                        refIndex  = index,
                        childCount, previousSibling, i;

                    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;
                        }
                        oldParent.removeChild(node, false, false, true);
                    }

                    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()) {
                        me.set('loaded', true);
                    }
                    
                    else if (me.childNodes.length === 1) {
                        me.triggerUIUpdate();
                    }

                    if(!node.isLeaf() && node.phantom) {
                        node.set('loaded', true);
                    }

                    if (suppressEvents !== true) {
                        me.fireEventArgs("insert", [me, node, refNode]);

                        if (oldParent) {
                            node.fireEventArgs("move", [node, oldParent, me, refIndex, refNode]);
                        }
                    }

                    return node;
                },

                
                insertChild: function(index, node) {
                    var sibling = this.childNodes[index];
                    if (sibling) {
                        return this.insertBefore(node, sibling);
                    }
                    else {
                        return this.appendChild(node);
                    }
                },

                
                remove : function(destroy, suppressEvents) {
                    var me = this,
                        parentNode = me.parentNode;

                    if (parentNode) {
                        parentNode.removeChild(me, destroy, suppressEvents);
                    } else if (destroy) {
                        
                        me.destroy(true);
                    }
                    return me;
                },

                
                removeAll : function(destroy, suppressEvents, fromParent) {
                    
                    
                    
                    var me = this,
                        childNodes = me.childNodes,
                        i = 0,
                        len = childNodes.length,
                        node;

                    
                    if (!len) {
                        return;
                    }

                    
                    
                    me.fireEventArgs('bulkremove', [me, childNodes, false]);

                    for (; i < len; ++i) {
                        node = childNodes[i];
                        
                        
                        node.removeContext = {
                            parentNode: node.parentNode,
                            previousSibling: node.previousSibling,
                            nextSibling: node.nextSibling
                        };

                        node.previousSibling = node.nextSibling = node.parentNode = null;
                        me.fireEventArgs('remove', [me, node, false]);

                        
                        node.removeContext = null;

                        
                        if (destroy) {
                            node.destroy(true);
                        }
                        
                        else {
                            node.removeAll(false, suppressEvents, true);
                        }
                    }

                    me.firstChild = me.lastChild = null;

                    
                    if (fromParent) {
                        
                        me.childNodes = null;
                    } else {
                        
                        me.childNodes.length = 0;
                        me.triggerUIUpdate();
                    }
                    
                    return me;
                },

                
                getChildAt : function(index) {
                    return this.childNodes[index];
                },

                
                replaceChild : function(newChild, oldChild, suppressEvents) {
                    var s = oldChild ? oldChild.nextSibling : null;

                    this.removeChild(oldChild, false, suppressEvents);
                    this.insertBefore(newChild, s, suppressEvents);
                    return oldChild;
                },

                
                indexOf : function(child) {
                    return Ext.Array.indexOf(this.childNodes, child);
                },
                
                
                indexOfId: function(id) {
                    var childNodes = this.childNodes,
                        len = childNodes.length,
                        i = 0;
                        
                    for (; i < len; ++i) {
                        if (childNodes[i].getId() === id) {
                            return i;
                        }    
                    }
                    return -1;
                },

                
                getPath: function(field, separator) {
                    field = field || this.idProperty;
                    separator = separator || '/';

                    var path = [this.get(field)],
                        parent = this.parentNode;

                    while (parent) {
                        path.unshift(parent.get(field));
                        parent = parent.parentNode;
                    }
                    return separator + path.join(separator);
                },

                
                getDepth : function() {
                    return this.get('depth');
                },

                
                bubble : function(fn, scope, args) {
                    var p = this;
                    while (p) {
                        if (fn.apply(scope || p, args || [p]) === false) {
                            break;
                        }
                        p = p.parentNode;
                    }
                },

                cascade: function() {
                    if (Ext.isDefined(Ext.global.console)) {
                        Ext.global.console.warn('Ext.data.Node: cascade has been deprecated. Please use cascadeBy instead.');
                    }
                    return this.cascadeBy.apply(this, arguments);
                },

                
                cascadeBy : function(fn, scope, args) {
                    if (fn.apply(scope || this, args || [this]) !== false) {
                        var childNodes = this.childNodes,
                            length     = childNodes.length,
                            i;

                        for (i = 0; i < length; i++) {
                            childNodes[i].cascadeBy(fn, scope, args);
                        }
                    }
                },

                
                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 cs  = this.childNodes,
                        ln = cs.length,
                        i, n, info = {
                            isFirst: true
                        };

                    if (ln > 0) {
                        Ext.Array.sort(cs, sortFn);
                        this.setFirstChild(cs[0]);
                        this.setLastChild(cs[ln - 1]);

                        for (i = 0; i < ln; i++) {
                            n = cs[i];
                            n.previousSibling = cs[i-1];
                            n.nextSibling = cs[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) {
                            this.fireEventArgs('sort', [this, cs]);
                        }
                    }
                },

                
                isExpanded: function() {
                    return this.get('expanded');
                },

                
                isLoaded: function() {
                    return this.get('loaded');
                },

                
                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,
                        owner;

                    
                    

                    
                    if (!me.isLeaf()) {
                        
                        if (me.isLoading()) {
                            me.on('expand', function() {
                                me.expand(recursive, callback, scope);
                            }, me, {single: true});
                        } else {
                            
                            if (!me.isExpanded()) {

                                
                                
                                
                                
                                me.fireEventArgs('beforeexpand', [me, me.onChildNodesAvailable, me, [recursive, callback, scope]]);
                            } else if (recursive) {
                                
                                owner = me.getOwnerTree();
                                me.expandChildren(true, owner ? owner.singleExpand : false, 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,
                        owner;

                    
                    
                    Ext.suspendLayouts();

                    
                    me.set('expanded', true);

                    
                    me.fireEventArgs('expand', [me, me.childNodes, false]);

                    
                    if (recursive) {
                        owner = me.getOwnerTree();
                        me.expandChildren(true, owner ? owner.singleExpand : false, callback, scope);
                    } else {
                        Ext.callback(callback, scope || me, [me.childNodes]);
                    }

                    Ext.resumeLayouts(true);
                },

                
                expandChildren: function(recursive, singleExpand, callback, scope) {
                    var me = this,
                        i,
                        allNodes = me.childNodes,
                        expandNodes = [],
                        ln = singleExpand ? Math.min(allNodes.length, 1) : allNodes.length,
                        node;

                    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(),
                        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, callback);
                                } else {
                                    callback = collapseChildren;
                                }
                            }

                            
                            me.set('expanded', false);

                            
                            
                            
                            me.fireEventArgs('collapse', [me, me.childNodes, false, callback ? Ext.Function.bind(callback, scope, [me.childNodes]) : null, null]);

                            
                            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, Ext.emptyFn]) !== false) {

                        
                        me.data.expanded = false;

                        
                        
                        
                        me.fireEventArgs('collapse', [me, me.childNodes, false, null, null]);

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

                
                
                
                fireEventArgs: function(eventName, args) {
                    
                    
                    
                    var fireEventArgs = Ext.data.Model.prototype.fireEventArgs,
                        result, eventSource, tree, treeStore, rootNode;

                    
                    if (bubbledEvents[eventName]) {
                        for (eventSource = this; result !== false && eventSource; eventSource = (rootNode = eventSource).parentNode) {
                            if (eventSource.hasListeners[eventName]) {
                                result = fireEventArgs.call(eventSource, eventName, args);
                            }
                        }

                        
                        tree = rootNode.rootOf;
                        if (result !== false && tree) {
                            treeStore = tree.treeStore;
                            if (treeStore && treeStore.hasListeners[eventName]) {
                                result = treeStore.fireEventArgs.call(treeStore, eventName, args);
                            }
                            if (result !== false && tree.hasListeners[eventName]) {
                                result = tree.fireEventArgs.call(tree, eventName, args);
                            }
                        }
                        return result;
                    }
                    
                    else {
                        return fireEventArgs.apply(this, arguments)
                    }
                },

                
                serialize: function() {
                    var result = Ext.data.writer.Json.prototype.getRecordData(this),
                        childNodes = this.childNodes,
                        len = childNodes.length,
                        children, i;

                    if (len > 0) {
                        children = [];
                        for (i = 0; i < len; i++) {
                            children.push(childNodes[i].serialize());
                        }
                        result.children = children;
                    }
                    return result;
                }
            };
        }
    }
});


Ext.define('Ext.data.NodeStore', {
    extend:  Ext.data.Store ,
    alias: 'store.node',
                                         

    
    isNodeStore: true,

    
    node: null,

    
    recursive: false,

    
    rootVisible: false,

    

    
    isExpandingOrCollapsing: 0,

    constructor: function(config) {
        var me = this,
            node;

        config = config || {};
        Ext.apply(me, config);


        config.proxy = {type: 'proxy'};
        me.callParent([config]);

        node = me.node;
        if (node) {
            me.node = null;
            me.setNode(node);
        }
    },

    
    
    
    getTotalCount: function() {
        return this.getCount();
    },

    setNode: function(node) {
        var me = this;
        if (me.node && me.node != node) {
            
            me.mun(me.node, {
                expand: me.onNodeExpand,
                collapse: me.onNodeCollapse,
                append: me.onNodeAppend,
                insert: me.onNodeInsert,
                bulkremove: me.onBulkRemove,
                remove: me.onNodeRemove,
                sort: me.onNodeSort,
                scope: me
            });
            me.node = null;
        }

        if (node) {
            Ext.data.NodeInterface.decorate(node.self);
            me.removeAll();
            if (me.rootVisible) {
                me.add(node);
            } else if (!node.isExpanded() && me.treeStore.autoLoad !== false) {
                node.expand();
            }

            me.mon(node, {
                expand: me.onNodeExpand,
                collapse: me.onNodeCollapse,
                append: me.onNodeAppend,
                insert: me.onNodeInsert,
                bulkremove: me.onBulkRemove,
                remove: me.onNodeRemove,
                sort: me.onNodeSort,
                scope: me
            });
            me.node = node;
            if (node.isExpanded() && node.isLoaded()) {
                me.onNodeExpand(node, node.childNodes, true);
            }
        }
    },
 
    onNodeSort: function(node, childNodes) {
        var me = this;

        if ((me.indexOf(node) !== -1 || (node === me.node && !me.rootVisible) && node.isExpanded())) {
            Ext.suspendLayouts();
            me.onNodeCollapse(node, childNodes, true);
            me.onNodeExpand(node, childNodes, true);
            Ext.resumeLayouts(true);
        }
    },

    
    onNodeExpand: function(parent, records, suppressEvent) {
        var me = this,
            insertIndex = me.indexOf(parent) + 1,
            toAdd = [];

        
        
        
        if (!suppressEvent) {
            me.fireEvent('beforeexpand', parent, records, insertIndex);
        }

        me.handleNodeExpand(parent, records, toAdd);

        
        
        
        me.insert(insertIndex, toAdd);

        
        
        if (!suppressEvent) {
            me.fireEvent('expand', parent, records);
        }
    },

    
    
    handleNodeExpand: function(parent, records, toAdd) {
        var me = this,
            ln = records ? records.length : 0,
            i, record;

        
        if (!me.recursive && parent !== me.node) {
            return;
        }

        if (parent !== this.node && !me.isVisible(parent)) {
            return;
        }

        if (ln) {
            
            
            for (i = 0; i < ln; i++) {
                record = records[i];

                
                
                
                toAdd.push(record);

                if (record.isExpanded()) {
                    if (record.isLoaded()) {
                        
                        me.handleNodeExpand(record, record.childNodes, toAdd);
                    }
                    else {
                        
                        record.set('expanded', false);
                        record.expand();
                    }
                }
            }
        }
    },
    
    
    onBulkRemove: function(parent, childNodes, isMove) {
        this.onNodeCollapse(parent, childNodes, true);
    },

    
    onNodeCollapse: function(parent, records, suppressEvent, callback, scope) {
        var me = this,
            collapseIndex = me.indexOf(parent) + 1,
            node, lastNodeIndexPlus, sibling, found;

        if (!me.recursive && parent !== me.node) {
            return;
        }

        
        
        
        
        if (!suppressEvent) {
            me.fireEvent('beforecollapse', parent, records, collapseIndex, callback, scope);
        }

        
        
        
        
        if (records.length && me.data.contains(records[0])) {
            
            
            
            
            node = parent;
            while (node.parentNode) {
                sibling = node.nextSibling;
                if (sibling) {
                    found = true;
                    lastNodeIndexPlus = me.indexOf(sibling); 
                    break;
                } else {
                    node = node.parentNode;
                }
            }
            if (!found) {
                lastNodeIndexPlus = me.getCount();
            }

            
            me.removeAt(collapseIndex, lastNodeIndexPlus - collapseIndex);
        }

        
        
        if (!suppressEvent) {
            me.fireEvent('collapse', parent, records, collapseIndex);
        }
    },

    onNodeAppend: function(parent, node, index) {
        var me = this,
            refNode, sibling;

        
        if (me.isVisible(node)) {
            if (index === 0) {
                refNode = parent;
            } else {
                sibling = node.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, true);
                } else if (!me.treeStore.fillCount ) {
                    
                    
                    
                    
                    node.set('expanded', false);
                    node.expand();
                }
            }
        }
    },

    onNodeInsert: function(parent, node, refNode) {
        var me = this,
            index = this.indexOf(refNode);

        if (index != -1 && me.isVisible(node)) {
            me.insert(index, node);
            if (!node.isLeaf() && node.isExpanded()) {
                if (node.isLoaded()) {
                    
                    me.onNodeExpand(node, node.childNodes, true);
                }
                else {
                    node.set('expanded', false);
                    node.expand();
                }
            }
        }
    },

    onNodeRemove: function(parent, node, isMove) {
        var me = this;
        if (me.indexOf(node) != -1) {

            
            
            if (!node.isLeaf() && node.isExpanded()) {

                
                
                
                
                node.parentNode = node.removeContext.parentNode;
                node.nextSibling = node.removeContext.nextSibling;
                me.onNodeCollapse(node, node.childNodes, true);
                node.parentNode = node.nextSibling = null;
            }
            me.remove(node);
        }
    },

    isVisible: function(node) {
        var parent = node.parentNode;
        while (parent) {
            
            if (parent === this.node && parent.data.expanded) {
                return true;
            }

            
            if (!parent.data.expanded) {
                return false;
            }

            parent = parent.parentNode;
        }
        
        return false;
    }
});


Ext.define('Ext.data.Request', {
    
    action: undefined,
    
    
    params: undefined,
    
    
    method: 'GET',
    
    
    url: undefined,

    
    constructor: function(config) {
        Ext.apply(this, config);
    }
});


Ext.define('Ext.data.SequentialIdGenerator', {
    extend:  Ext.data.IdGenerator ,
    alias: 'idgen.sequential',

    constructor: function() {
        var me = this;

        me.callParent(arguments);

        me.parts = [ me.prefix, ''];
    },

    
    prefix: '',

    
    seed: 1,

    
    generate: function () {
        var me = this,
            parts = me.parts;

        parts[1] = me.seed++;
        return parts.join('');
    }
});


Ext.define('Ext.data.Tree', {
    alias: 'data.tree',

    mixins: {
        observable:  Ext.util.Observable 
    },

    
    root: null,

    
    constructor: function(root) {
        var me = this;

        me.mixins.observable.constructor.call(me);

        if (root) {
            me.setRootNode(root);
        }
        
        
        me.on({
            scope: me,
            idchanged: me.onNodeIdChanged,
            insert: me.onNodeInsert,
            append: me.onNodeAppend,
            remove: me.onNodeRemove
        });
    },

    
    getRootNode : function() {
        return this.root;
    },

    
    setRootNode : function(node) {
        var me = this;

        me.root = node;

        
        if (node.rootOf) {
            node.rootOf.removeRootNode();
        }

        
        else if (node.parentNode) {
            node.parentNode.removeChild(node);
        }

        
        node.rootOf = me;

        if (node.fireEventArgs('beforeappend', [null, node]) !== false) {
            node.set('root', true);
            
            node.updateInfo(true, {
                isFirst: true,
                isLast: true,
                depth: 0,
                index: 0,
                parentId: null
            });

            
            
            
            
            
            
            
            
            
            
            
            
            
            
            

            me.nodeHash = {};
            node.fireEvent('append', null, node);
            node.fireEvent('rootchange', node);
        }

        return node;
    },

    
    removeRootNode: function() {
        var me = this,
            root = me.root;

        root.set('root', false);
        root.fireEvent('remove', null, root, false);
        root.fireEvent('rootchange', null);

        
        
        root.rootOf = me.root = null;
        return root;
    },

    
    flatten: function(){
        return Ext.Object.getValues(this.nodeHash);
    },

    
    onNodeInsert: function(parent, node) {
        this.registerNode(node, true);
    },

    
    onNodeAppend: function(parent, node) {
        this.registerNode(node, true);
    },

    
    onNodeRemove: function(parent, node) {
        this.unregisterNode(node, true);
    },

    
    onNodeIdChanged: function(node, oldId, newId, oldInternalId) {
        var nodeHash = this.nodeHash;
    
        nodeHash[node.internalId] = node;
        delete nodeHash[oldInternalId];
    },

    
    getNodeById : function(id) {
        return this.nodeHash[id];
    },

    
    registerNode : function(node, includeChildren) {
        var me = this,
            children, length, i;

        me.nodeHash[node.internalId] = node;
        if (includeChildren === true) {
            children = node.childNodes;
            length = children.length;
            for (i = 0; i < length; i++) {
                me.registerNode(children[i], true);
            }
        }
    },

    
    unregisterNode : function(node, includeChildren) {
        var me = this,
            children, length, i;

        delete me.nodeHash[node.internalId];
        if (includeChildren === true) {
            children = node.childNodes;
            length = children.length;
            for (i = 0; i < length; i++) {
                me.unregisterNode(children[i], true);
            }
        }
    },

    
    sort: function(sorterFn, recursive) {
        this.getRootNode().sort(sorterFn, recursive);
    },

     
    filter: function(filters, recursive) {
        this.getRootNode().filter(filters, recursive);
    }
});


Ext.define('Ext.data.TreeModel', {
    extend:  Ext.data.Model  
               
                                
     
},
function () {
    Ext.data.NodeInterface.decorate(this);
});


Ext.define('Ext.data.TreeStore', {
    extend:  Ext.data.AbstractStore ,
    alias: 'store.tree',
               
                          
                        
                                
      

    

    
    clearOnLoad : true,

    
    clearRemovedOnLoad: true,

    
    nodeParam: 'node',

    
    defaultRootId: 'root',
    
    
    defaultRootText: 'Root',

    
    defaultRootProperty: 'children',
    
    
    rootProperty: 'children',
    
    fillCount: 0,

    
    folderSort: false,

    constructor: function(config) {
        var me = this,
            root,
            fields,
            defaultRoot;

        config = Ext.apply({}, config);

        
        fields = config.fields || me.fields;
        if (!fields) {
            config.fields = [
                {name: 'text', type: 'string'}
            ];
            defaultRoot = config.defaultRootProperty || me.defaultRootProperty;
            if (defaultRoot !== me.defaultRootProperty) {
                config.fields.push({
                    name: defaultRoot,   
                    type: 'auto',   
                    defaultValue: null, 
                    persist: false
                });
            }
        }

        me.callParent([config]);

        
        me.tree = new Ext.data.Tree();
        
        
        me.tree.treeStore = me;

        
        
        
        
        
        
        
        
        
        
        
        
        
        

        me.tree.on({
            scope: me,
            remove: me.onNodeRemove,
            
            
            beforeexpand: me.onBeforeNodeExpand,
            append: me.onNodeAdded,
            insert: me.onNodeAdded,
            sort: me.onNodeSort
        });

        me.onBeforeSort();

        root = me.root;
        if (root) {
            delete me.root;
            me.setRootNode(root);
        }

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

    
    setProxy: function(proxy) {
        var reader,
            needsRoot;

        if (proxy instanceof Ext.data.proxy.Proxy) {
            
            needsRoot = Ext.isEmpty(proxy.getReader().root);
        } else if (Ext.isString(proxy)) {
            
            needsRoot = true;
        } else {
            
            reader = proxy.reader;
            needsRoot = !(reader && !Ext.isEmpty(reader.root));
        }
        proxy = this.callParent(arguments);

        
        
        
        proxy.idParam = this.nodeParam;

        if (needsRoot) {
            reader = proxy.getReader();
            reader.root = this.defaultRootProperty;
            
            reader.buildExtractors(true);
        }
        return proxy;
    },

    
    onBeforeSort: function() {
        if (this.folderSort) {
            this.sort({
                property: 'leaf',
                direction: 'ASC'
            }, 'prepend', false);
        }
    },

    
    onBeforeNodeExpand: function(node, callback, scope, args) {
        var me = this,
            reader, dataRoot, data,
            callbackArgs;
        
        
        if (node.isLoaded()) {
            callbackArgs = [node.childNodes];
            if (args) {
                callbackArgs.push.apply(callbackArgs, args);
            }
            Ext.callback(callback, scope || node, callbackArgs);
        }
        
        else if (dataRoot = (data = (node.raw || node[node.persistenceProperty])[(reader = me.getProxy().getReader()).root])) {
            me.fillNode(node, reader.extractData(dataRoot));
            delete data[reader.root];
            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 {
            me.read({
                node: node,
                
                
                internalCallback: function() {
                    
                    
                    delete me.lastOptions.internalCallback;
                    callbackArgs = [node.childNodes];
                    if (args) {
                        callbackArgs.push.apply(callbackArgs, args);
                    }
                    Ext.callback(callback, scope || node, callbackArgs);
                }
            });
        }
    },

    
    getNewRecords: function() {
        return Ext.Array.filter(this.tree.flatten(), this.filterNew);
    },

    
    getUpdatedRecords: function() {
        return Ext.Array.filter(this.tree.flatten(), this.filterUpdated);
    },

    onNodeRemove: function(parent, node, isMove) {
        var me = this;

        node.unjoin(me);
        
        
        
        if (!node.phantom && !isMove) {
            Ext.Array.include(me.removed, node);
        }

        if (me.autoSync && !me.autoSyncSuspended && !isMove) {
            me.sync();
        }
    },

    onNodeAdded: function(parent, node) {
        var me = this,
            proxy = me.getProxy(),
            reader = proxy.getReader(),
            data = node.raw || node[node.persistenceProperty],
            dataRoot;

        Ext.Array.remove(me.removed, node);
        node.join(me);

        
        if (!node.isLeaf() && !me.lazyFill) {
            dataRoot = reader.getRoot(data);
            if (dataRoot) {
                me.fillNode(node, reader.extractData(dataRoot));
                delete data[reader.root];
            }
        }

        if (me.autoSync && !me.autoSyncSuspended && (node.phantom || node.dirty)) {
            me.sync();
        }
    },

    onNodeSort: function() {
        if (this.autoSync && !this.autoSyncSuspended) {
            this.sync();
        }
    },

    
    setRootNode: function(root,  preventLoad) {
        var me = this,
            model = me.model,
            idProperty = model.prototype.idProperty

        root = root || {};
        if (!root.isModel) {
            root = Ext.apply({}, root);
            
            Ext.applyIf(root, {
                id: me.defaultRootId,
                text: me.defaultRootText,
                allowDrag: false
            });
            if (root[idProperty] === undefined) {
                root[idProperty] = me.defaultRootId;
            }
            Ext.data.NodeInterface.decorate(model);
            root = Ext.ModelManager.create(root, model);
        } else if (root.isModel && !root.isNode) {
            Ext.data.NodeInterface.decorate(model);
        }


        
        
        me.getProxy().getReader().buildExtractors(true);

        
        me.tree.setRootNode(root);

        
        
        
        
        
        if (preventLoad !== true && !root.isLoaded() && (me.autoLoad === true || root.isExpanded())) {
            root.data.expanded = false;
            root.expand();
        }

        return root;
    },

    
    getRootNode: function() {
        return this.tree.getRootNode();
    },

    
    getNodeById: function(id) {
        return this.tree.getNodeById(id);
    },

    
    getById: function(id) {
        return this.getNodeById(id);
    },

    
    load: function(options) {
        options = options || {};
        options.params = options.params || {};

        var me = this,
            node = options.node || me.tree.getRootNode(),
            callback = options.callback,
            scope = options.scope,
            operation;

        
        
        if (!node) {
            node = me.setRootNode({
                expanded: true
            }, true);
        }

        
        if (node.data.expanded) {
            node.data.loaded = false;

            
            
            if (me.clearOnLoad) {
                node.data.expanded = false;
            }
            options.callback = function() {

                
                
                
                if (!me.clearOnLoad) {
                    node.collapse();
                }
                node.expand();

                
                Ext.callback(callback, scope, arguments);
            }
        }

        
        
        options.id = node.getId();

        options = Ext.apply({
            action: 'read',
            filters: me.filters.items,
            sorters: me.getSorters(),
            node: options.node || node
        }, options);

        me.lastOptions = options;

        operation = new Ext.data.Operation(options);

        if (me.fireEvent('beforeload', me, operation) !== false) {
             if (me.clearOnLoad) {
                if(me.clearRemovedOnLoad) {
                    
                    me.clearRemoved(node);
                }
                
                me.tree.un('remove', me.onNodeRemove, me);
                
                node.removeAll(false);
                
                me.tree.on('remove', me.onNodeRemove, me);
            }
            me.loading = true;
            me.proxy.read(operation, me.onProxyLoad, me);
        }

        if (me.loading && node) {
            node.set('loading', true);
        }

        return me;
    },

    
    clearRemoved: function(node) {
        var me = this,
            removed = me.removed,
            id = node.getId(),
            removedLength = removed.length,
            i = removedLength,
            recordsToClear = {},
            newRemoved = [],
            removedHash = {},
            removedNode,
            targetNode,
            targetId;

        if(node === me.getRootNode()) {
            
            me.removed = [];
            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 = 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.removed = newRemoved;
    },

    
    fillNode: function(node, newNodes) {
        var me = this,
            ln = newNodes ? newNodes.length : 0,
            sorters = me.sorters,
            i, sortCollection,
            needsIndexSort = false,
            performLocalSort = ln && me.sortOnLoad && !me.remoteSort && sorters && sorters.items && sorters.items.length,
            node1, node2, rootFill;

        
        for (i = 1; i < ln; i++) {
            node1 = newNodes[i];
            node2 = newNodes[i - 1];
            needsIndexSort = node1[node1.persistenceProperty].index != node2[node2.persistenceProperty].index;
            if (needsIndexSort) {
                break;
            }
        }

        
        if (performLocalSort) {
            
            if (needsIndexSort) {
                me.sorters.insert(0, me.indexSorter);
            }
            sortCollection = new Ext.util.MixedCollection();
            sortCollection.addAll(newNodes);
            sortCollection.sort(me.sorters.items);
            newNodes = sortCollection.items;

            
            me.sorters.remove(me.indexSorter);
        } else if (needsIndexSort) {
            Ext.Array.sort(newNodes, me.sortByIndex);
        }

        node.set('loaded', true);
        
        
        
        
        
        rootFill = me.fillCount === 0;
        if (rootFill) {
            
            me.fireEvent('beforefill', me, node, newNodes);
        }
        ++me.fillCount;

        if (newNodes.length) {
            node.appendChild(newNodes, undefined, true);
        }
        
        if (rootFill) {
            
            me.fireEvent('fillcomplete', me, node, newNodes);
        }
        --me.fillCount;
        
        return newNodes;
    },

    
    sortByIndex: function(node1, node2) {
        return node1[node1.persistenceProperty].index - node2[node2.persistenceProperty].index;
    },
    
    onIdChanged: function(model, oldId, newId, oldInternalId){
        this.tree.onNodeIdChanged(model, oldId, newId, oldInternalId);
        this.callParent(arguments);    
    },

    
    onProxyLoad: function(operation) {
        var me = this,
            successful = operation.wasSuccessful(),
            records = operation.getRecords(),
            node = operation.node,
            scope = operation.scope || me,
            args = [records, operation, successful];

        me.loading = false;
        node.set('loading', false);
        if (successful) {
            if (!me.clearOnLoad) {
                records = me.cleanRecords(node, records);
            }
            records = me.fillNode(node, records);
        }
        
        
        
        
        Ext.callback(operation.internalCallback, scope, args);
        me.fireEvent('read', me, operation.node, records, successful);
        me.fireEvent('load', me, operation.node, records, successful);
        
        Ext.callback(operation.callback, scope, args);
    },
    
    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() {
        var root = this.getRootNode();
        if (root) {
            root.destroy(true);
        }
        this.fireEvent('clear', this);
    },

    
    doSort: function(sorterFn) {
        var me = this;
        if (me.remoteSort) {
            
            me.load();
        } else {
            me.tree.sort(sorterFn, true);
            me.fireEvent('datachanged', me);
            me.fireEvent('refresh', me);
        }
        me.fireEvent('sort', me, me.sorters.getRange());
    }
}, function() {
    var proto = this.prototype;
    proto.indexSorter = new Ext.util.Sorter({
        sorterFn: proto.sortByIndex
    });
});


Ext.define('Ext.data.UuidGenerator', (function () {
    var twoPow14 = Math.pow(2, 14),
        twoPow16 = Math.pow(2, 16),
        twoPow28 = Math.pow(2, 28),
        twoPow32 = Math.pow(2, 32);

    function toHex (value, length) {
        var ret = value.toString(16);
        if (ret.length > length) {
            ret = ret.substring(ret.length - length); 
        } else if (ret.length < length) {
            ret = Ext.String.leftPad(ret, length, '0');
        }
        return ret;
    }

    function rand (lo, hi) {
        var v = Math.random() * (hi - lo + 1);
        return Math.floor(v) + lo;
    }

    function split (bignum) {
        if (typeof(bignum) == 'number') {
            var hi = Math.floor(bignum / twoPow32);
            return {
                lo: Math.floor(bignum - hi * twoPow32),
                hi: hi
            };
        }
        return bignum;
    }

    return {
        extend:  Ext.data.IdGenerator ,

        alias: 'idgen.uuid',

        id: 'uuid', 

        

        

        
        version: 4,

        constructor: function() {
            var me = this;

            me.callParent(arguments);

            me.parts = [];
            me.init();
        },

        generate: function () {
            var me = this,
                parts = me.parts,
                ts = me.timestamp;

            
            parts[0] = toHex(ts.lo, 8);
            parts[1] = toHex(ts.hi & 0xFFFF, 4);
            parts[2] = toHex(((ts.hi >>> 16) & 0xFFF) | (me.version << 12), 4);
            parts[3] = toHex(0x80 | ((me.clockSeq >>> 8) & 0x3F), 2) +
                       toHex(me.clockSeq & 0xFF, 2);
            parts[4] = toHex(me.salt.hi, 4) + toHex(me.salt.lo, 8);

            if (me.version == 4) {
                me.init(); 
            } else {
                
                ++ts.lo;
                if (ts.lo >= twoPow32) { 
                    ts.lo = 0;
                    ++ts.hi;
                }
            }

            return parts.join('-').toLowerCase();
        },

        getRecId: function (rec) {
            return rec.getId();
        },

        
        init: function () {
            var me = this,
                salt, time;

            if (me.version == 4) {
                
                
                
                
                me.clockSeq = rand(0, twoPow14-1);

                
                salt = me.salt || (me.salt = {});
                time = me.timestamp || (me.timestamp = {});

                
                salt.lo = rand(0, twoPow32-1);
                salt.hi = rand(0, twoPow16-1);
                time.lo = rand(0, twoPow32-1);
                time.hi = rand(0, twoPow28-1);
            } else {
                
                me.salt = split(me.salt);
                me.timestamp = split(me.timestamp);

                
                
                me.salt.hi |= 0x100;
            }
        },

        
        reconfigure: function (config) {
            Ext.apply(this, config);
            this.init();
        }
    };
}()));


Ext.define('Ext.data.reader.Xml', {
    extend:  Ext.data.reader.Reader ,
    alternateClassName: 'Ext.data.XmlReader',
    alias : 'reader.xml',

    

    

    
    createAccessor: function(expr) {
        var me = this;

        if (Ext.isEmpty(expr)) {
            return Ext.emptyFn;
        }

        if (Ext.isFunction(expr)) {
            return expr;
        }

        return function(root) {
            return me.getNodeValue(Ext.DomQuery.selectNode(expr, root));
        };
    },

    getNodeValue: function(node) {
        if (node) {
            
            
            
            if (typeof node.normalize === 'function') {
                node.normalize();
            }
            node = node.firstChild;
            if (node) {
                return node.nodeValue;
            }
        }
        return undefined;
    },

    
    getResponseData: function(response) {
        var xml = response.responseXML,
            error,
            msg;

        if (!xml) {
            msg = 'XML data not found in the response';               

            error = new Ext.data.ResultSet({
                total  : 0,
                count  : 0,
                records: [],
                success: false,
                message: msg
            });

            this.fireEvent('exception', this, response, error);

            Ext.Logger.warn(msg);

            return error;
        }

        return this.readRecords(xml);
    },

    
    getData: function(data) {
        return data.documentElement || data;
    },

    
    getRoot: function(data) {
        var nodeName = data.nodeName,
            root     = this.root;

        if (!root || (nodeName && nodeName == root)) {
            return data;
        } else if (Ext.DomQuery.isXml(data)) {
            
            
            
            return Ext.DomQuery.selectNode(root, data);
        }
    },

    
    extractData: function(root) {
        var recordName = this.record;


        if (recordName != root.nodeName) {
            root = Ext.DomQuery.select(recordName, root);
        } else {
            root = [root];
        }
        return this.callParent([root]);
    },

    
    getAssociatedDataRoot: function(data, associationName) {
        return Ext.DomQuery.select(associationName, data)[0];
    },

    
    readRecords: function(doc) {
        
        
        if (Ext.isArray(doc)) {
            doc = doc[0];
        }

        
        this.xmlData = doc;
        return this.callParent([doc]);
    },

    
    createFieldAccessExpression: function(field, fieldVarName, dataName) {
        var namespace = this.namespace,
            selector, result;

        selector = field.mapping || ((namespace ? namespace + '|' : '') + field.name); 

        if (typeof selector === 'function') {
            result = fieldVarName + '.mapping(' + dataName + ', this)';
        } else {
            result = 'me.getNodeValue(Ext.DomQuery.selectNode("' + selector + '", ' + dataName + '))';
        }
        return result;
    }
});


Ext.define('Ext.data.writer.Xml', {
    
    
    
    extend:  Ext.data.writer.Writer ,
    alternateClassName: 'Ext.data.XmlWriter',
    
    alias: 'writer.xml',
    
    
    
    
    documentRoot: 'xmlData',
    
    
    defaultDocumentRoot: 'xmlData',

    
    header: '',

    
    record: 'record',

    
    writeRecords: function(request, data) {
        var me = this,
            xml = [],
            i = 0,
            len = data.length,
            root = me.documentRoot,
            record = me.record,
            needsRoot = data.length !== 1,
            item,
            key;
            
        
        xml.push(me.header || '');
        
        if (!root && needsRoot) {
            root = me.defaultDocumentRoot;
        }
        
        if (root) {
            xml.push('<', root, '>');
        }
            
        for (; i < len; ++i) {
            item = data[i];
            xml.push('<', record, '>');
            for (key in item) {
                if (item.hasOwnProperty(key)) {
                    xml.push('<', key, '>', item[key], '</', key, '>');
                }
            }
            xml.push('</', record, '>');
        }
        
        if (root) {
            xml.push('</', root, '>');
        }
            
        request.xmlData = xml.join('');
        return request;
    }
});


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.association.BelongsTo', {
    extend:  Ext.data.association.Association ,
    alternateClassName: 'Ext.data.BelongsToAssociation',
    alias: 'association.belongsto',

    

    

    

    
    constructor: function(config) {
        this.callParent(arguments);

        var me             = this,
            ownerProto     = me.ownerModel.prototype,
            associatedName = me.associatedName,
            getterName     = me.getterName || 'get' + associatedName,
            setterName     = me.setterName || 'set' + associatedName;

        Ext.applyIf(me, {
            name        : associatedName,
            foreignKey  : associatedName.toLowerCase() + "_id",
            instanceName: associatedName + 'BelongsToInstance',
            associationKey: associatedName.toLowerCase()
        });

        ownerProto[getterName] = me.createGetter();
        ownerProto[setterName] = me.createSetter();
    },

    
    createSetter: function() {
        var me = this,
            foreignKey = me.foreignKey,
            instanceName = me.instanceName;

        
        return function(value, options, scope) {
            
            var setByRecord = value && value.isModel,
                valueToSet = setByRecord ? value.getId() : value;

            
            if (setByRecord) {
                this[instanceName] = value;
            }

            
            else if (this[instanceName] instanceof Ext.data.Model && !this.isEqual(this.get(foreignKey), valueToSet)) {
                delete this[instanceName];
            }

            
            this.set(foreignKey, valueToSet);

            if (Ext.isFunction(options)) {
                options = {
                    callback: options,
                    scope: scope || this
                };
            }

            if (Ext.isObject(options)) {
                return this.save(options);
            }
        };
    },

    
    createGetter: function() {
        var me              = this,
            associatedName  = me.associatedName,
            associatedModel = me.associatedModel,
            foreignKey      = me.foreignKey,
            primaryKey      = me.primaryKey,
            instanceName    = me.instanceName;

        
        return function(options, scope) {
            options = options || {};

            var model = this,
                foreignKeyId = model.get(foreignKey),
                success,
                instance,
                args;

            if (options.reload === true || model[instanceName] === undefined) {
                instance = Ext.ModelManager.create({}, associatedName);
                instance.set(primaryKey, foreignKeyId);

                if (typeof options == 'function') {
                    options = {
                        callback: options,
                        scope: scope || model
                    };
                }
                
                
                success = options.success;
                options.success = function(rec){
                    model[instanceName] = rec;
                    if (success) {
                        success.apply(this, arguments);
                    }
                };

                associatedModel.load(foreignKeyId, options);
                
                model[instanceName] = instance;
                return instance;
            } else {
                instance = model[instanceName];
                args = [instance];
                scope = scope || options.scope || model;

                
                
                
                Ext.callback(options, scope, args);
                Ext.callback(options.success, scope, args);
                Ext.callback(options.failure, scope, args);
                Ext.callback(options.callback, scope, args);

                return instance;
            }
        };
    },

    
    read: function(record, reader, associationData){
        record[this.instanceName] = reader.read([associationData]).records[0];
    }
});


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"   ],
        [(/^person$/),                "people" ],
        [(/^man$/),                   "men"    ],
        [(/^(child)$/),               "$1ren"  ],
        [(/s$/i),                     "s"      ],
        [(/$/),                       "s"      ]
    ],

    
    singulars: [
      [(/(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"  ],
      [(/people$/i),                                                       "person" ],
      [(/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) {
        this.plural(singular, irregulars[singular]);
        this.singular(irregulars[singular], singular);
    }
});


Ext.define('Ext.data.association.HasMany', {
    extend:  Ext.data.association.Association ,
    alternateClassName: 'Ext.data.HasManyAssociation',
                                     

    alias: 'association.hasmany',

    
    
    
    
    
    
    
    
    
    
    
    
    constructor: function(config) {
        var me = this,
            ownerProto,
            name;
            
        me.callParent(arguments);
        
        me.name = me.name || Ext.util.Inflector.pluralize(me.associatedName.toLowerCase());
        
        ownerProto = me.ownerModel.prototype;
        name = me.name;
        
        Ext.applyIf(me, {
            storeName : name + "Store",
            foreignKey: me.ownerName.toLowerCase() + "_id"
        });
        
        ownerProto[name] = me.createStore();
    },
    
    
    createStore: function() {
        var that            = this,
            associatedModel = that.associatedModel,
            storeName       = that.storeName,
            foreignKey      = that.foreignKey,
            primaryKey      = that.primaryKey,
            filterProperty  = that.filterProperty,
            autoLoad        = that.autoLoad,
            storeConfig     = that.storeConfig || {};
        
        return function() {
            var me = this,
                config, filter,
                modelDefaults = {},
                id;
                
            if (me[storeName] === undefined) {
                id = me.get(primaryKey);
                if (filterProperty) {
                    filter = {
                        property  : filterProperty,
                        value     : me.get(filterProperty),
                        exactMatch: true
                    };
                } else if (me.hasId(id)) {
                    
                    
                    filter = {
                        property  : foreignKey,
                        value     : id,
                        exactMatch: true
                    };
                }
                
                modelDefaults[foreignKey] = me.get(primaryKey);
                
                config = Ext.apply({}, storeConfig, {
                    model        : associatedModel,
                    filters      : filter ? [filter] : undefined,
                    remoteFilter : false,
                    modelDefaults: modelDefaults,
                    disableMetaChangeEvent: true
                });
                
                me[storeName] = Ext.data.AbstractStore.create(config);
                if (autoLoad) {
                    me[storeName].load();
                }
            }
            
            return me[storeName];
        };
    },
    
    
    read: function(record, reader, associationData){
        var store = record[this.name](),
            inverse,
            items, iLen, i;
    
        store.add(reader.read(associationData).records);
    
        
        
        inverse = this.associatedModel.prototype.associations.findBy(function(assoc){
            return assoc.type === 'belongsTo' && assoc.associatedName === record.$className;
        });
    
        
        if (inverse) {
            items = store.data.items;
            iLen  = items.length;

            for (i = 0; i < iLen; i++) {
                items[i][inverse.instanceName] = record;
            }
        }
    }
});


Ext.define('Ext.data.association.HasOne', {
    extend:  Ext.data.association.Association ,
    alternateClassName: 'Ext.data.HasOneAssociation',

    alias: 'association.hasone',
    
    

    

    

    
    
    constructor: function(config) {
        this.callParent(arguments);

        var me             = this,
            ownerProto     = me.ownerModel.prototype,
            associatedName = me.associatedName,
            getterName     = me.getterName || 'get' + associatedName,
            setterName     = me.setterName || 'set' + associatedName;

        Ext.applyIf(me, {
            name        : associatedName,
            foreignKey  : associatedName.toLowerCase() + "_id",
            instanceName: associatedName + 'HasOneInstance',
            associationKey: associatedName.toLowerCase()
        });

        ownerProto[getterName] = me.createGetter();
        ownerProto[setterName] = me.createSetter();
    },
    
    
    createSetter: function() {
        var me              = this,
            foreignKey      = me.foreignKey,
            instanceName = me.instanceName;

        
        return function(value, options, scope) {
            
            var setByRecord = value && value.isModel,
                valueToSet = setByRecord ? value.getId() : value;

            
            if (setByRecord) {
                this[instanceName] = value;
            }

            
            else if (this[instanceName] instanceof Ext.data.Model && !this.isEqual(this.get(foreignKey), valueToSet)) {
                delete this[instanceName];
            }

            
            this.set(foreignKey, valueToSet);

            if (Ext.isFunction(options)) {
                options = {
                    callback: options,
                    scope: scope || this
                };
            }

            if (Ext.isObject(options)) {
                return this.save(options);
            }
        };
    },

    
    createGetter: function() {
        var me              = this,
            ownerModel      = me.ownerModel,
            associatedName  = me.associatedName,
            associatedModel = me.associatedModel,
            foreignKey      = me.foreignKey,
            primaryKey      = me.primaryKey,
            instanceName    = me.instanceName;

        
        return function(options, scope) {
            options = options || {};

            var model = this,
                foreignKeyId = model.get(foreignKey),
                success,
                instance,
                args;

            if (options.reload === true || model[instanceName] === undefined) {
                instance = Ext.ModelManager.create({}, associatedName);
                instance.set(primaryKey, foreignKeyId);

                if (typeof options == 'function') {
                    options = {
                        callback: options,
                        scope: scope || model
                    };
                }
                
                
                success = options.success;
                options.success = function(rec){
                    model[instanceName] = rec;
                    if (success) {
                        success.apply(this, arguments);
                    }
                };

                associatedModel.load(foreignKeyId, options);
                
                model[instanceName] = instance;
                return instance;
            } else {
                instance = model[instanceName];
                args = [instance];
                scope = scope || options.scope || model;

                
                
                
                Ext.callback(options, scope, args);
                Ext.callback(options.success, scope, args);
                Ext.callback(options.failure, scope, args);
                Ext.callback(options.callback, scope, args);

                return instance;
            }
        };
    },
    
    
    read: function(record, reader, associationData){
        var inverse = this.associatedModel.prototype.associations.findBy(function(assoc){
            return assoc.type === 'belongsTo' && assoc.associatedName === record.$className;
        }), newRecord = reader.read([associationData]).records[0];
        
        record[this.instanceName] = newRecord;
    
        
        if (inverse) {
            newRecord[inverse.instanceName] = record;
        }
    }
});


Ext.define('Ext.data.proxy.WebStorage', {
    extend:  Ext.data.proxy.Client ,
    alternateClassName: 'Ext.data.WebStorageProxy',
               
                                        
      

    
    id: undefined,

    

    

    
    constructor: function(config) {
        this.callParent(arguments);

        
        this.cache = {};


        
        this.id = this.id || (this.store ? this.store.storeId : undefined);


        this.initialize();
    },

    
    create: function(operation, callback, scope) {
        var me = this,
            records = operation.records,
            length = records.length,
            ids = me.getIds(),
            id, record, i;

        operation.setStarted();

        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;
                id = me.getNextId();
            } else {
                id = record.getId();
            }

            me.setRecord(record, id);
            record.commit();
            ids.push(id);
        }

        me.setIds(ids);

        operation.setCompleted();
        operation.setSuccessful();

        if (typeof callback == 'function') {
            callback.call(scope || me, operation);
        }
    },

    
    read: function(operation, callback, scope) {
        

        var me = this,
            records = [],
            i = 0,
            success = true,
            Model = me.model,
            ids, length, record, data, id;

        operation.setStarted();

        if(me.isHierarchical) {
            records = me.getTreeData();
        } else {
            ids = me.getIds();
            length = ids.length;
            id = operation.id;
            
            if (id) {
                data = me.getRecord(id);
                if (data !== null) {
                    record = new Model(data, id, data);
                }

                if (record) {
                    records.push(record);
                } else {
                    success = false;
                }
            } else {
                for (; i < length; i++) {
                    id = ids[i];
                    data = me.getRecord(id);
                    records.push(new Model(data, id, data));
                }
            }

        }

        if(success) {
            operation.setSuccessful();
        }
        operation.setCompleted();

        operation.resultSet = Ext.create('Ext.data.ResultSet', {
            records: records,
            total  : records.length,
            loaded : true
        });

        if (typeof callback == 'function') {
            callback.call(scope || me, operation);
        }
    },

    
    update: function(operation, callback, scope) {
        var records = operation.records,
            length  = records.length,
            ids     = this.getIds(),
            record, id, i;

        operation.setStarted();

        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.setCompleted();
        operation.setSuccessful();

        if (typeof callback == 'function') {
            callback.call(scope || this, operation);
        }
    },

    
    destroy: function(operation, callback, scope) {
        var me = this,
            records = operation.records,
            ids = me.getIds(),
            idLength = ids.length,
            newIds = [],
            removedHash = {},
            i = records.length,
            id;

        operation.setStarted();

        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.setCompleted();
        operation.setSuccessful();

        if (typeof callback == 'function') {
            callback.call(scope || me, operation);
        }
    },

    
    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.model.prototype.idProperty] = id;

        return data;
    },

    
    setRecord: function(record, id) {
        if (id) {
            record.setId(id);
        } else {
            id = record.getId();
        }

        var me = this,
            rawData = record.data,
            data    = {},
            model   = me.model,
            fields  = model.prototype.fields.items,
            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[me.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.id, id);
    },

    
    getRecordCounterKey: function() {
        return Ext.String.format("{0}-counter", this.id);
    },

    
    getTreeKey: function() {
        return Ext.String.format("{0}-tree", this.id);
    },

    
    getIds: function() {
        var me = this,
            ids = (me.getStorageObject().getItem(me.id) || "").split(","),
            model = me.model,
            length = ids.length,
            isString = model.prototype.fields.get(model.prototype.idProperty).type.type === 'string',
            i;

        if (length == 1 && ids[0] === "") {
            ids = [];
        } else {
            for (i = 0; i < length; i++) {
                ids[i] = isString ? ids[i] : +ids[i];
            }
        }

        return ids;
    },

    
    setIds: function(ids) {
        var obj = this.getStorageObject(),
            str = ids.join(",");

        obj.removeItem(this.id);

        if (!Ext.isEmpty(str)) {
            obj.setItem(this.id, str);
        }
    },

    
    getNextId: function() {
        var me = this,
            obj = me.getStorageObject(),
            key = me.getRecordCounterKey(),
            model = me.model,
            isString = model.prototype.fields.get(model.prototype.idProperty).type.type === 'string',
            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.model,
            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, record[idProperty], 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());

        storageObject.setItem(me.id, storageObject.getItem(me.id) || "");
        if(storageObject.getItem(me.getTreeKey())) {
            me.isHierarchical = true;
        }

        me.idGenerator = new Ext.data.SequentialIdGenerator({
            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.id);

        
        me.cache = {};
    },

    
    getStorageObject: function() {
    }
});


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',

    
    actionMethods: {
        create : 'POST',
        read   : 'GET',
        update : 'PUT',
        destroy: 'DELETE'
    },
    
    defaultActionMethods: {
        create : 'POST',
        read   : 'GET',
        update : 'PUT',
        destroy: 'DELETE' 
    },

    
    appendId: true,

    

    
    batchActions: false,

    
    buildUrl: function(request) {
        var me        = this,
            operation = request.operation,
            records   = operation.records || [],
            record    = records[0],
            format    = me.format,
            url       = me.getUrl(request),
            id        = record ? record.getId() : operation.id;

        if (me.appendId && me.isValidId(id)) {
            if (!url.match(/\/$/)) {
                url += '/';
            }

            url += id;
        }

        if (format) {
            if (!url.match(/\.$/)) {
                url += '.';
            }

            url += format;
        }

        request.url = url;

        return me.callParent(arguments);
    },
    
    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.dd.DDTarget', {
    extend:  Ext.dd.DragDrop ,

    
    constructor: function(id, sGroup, config) {
        if (id) {
            this.initTarget(id, sGroup, config);
        }
    },

    
    getDragEl: Ext.emptyFn,
    
    isValidHandleChild: Ext.emptyFn,
    
    startDrag: Ext.emptyFn,
    
    endDrag: Ext.emptyFn,
    
    onDrag: Ext.emptyFn,
    
    onDragDrop: Ext.emptyFn,
    
    onDragEnter: Ext.emptyFn,
    
    onDragOut: Ext.emptyFn,
    
    onDragOver: Ext.emptyFn,
    
    onInvalidDrop: Ext.emptyFn,
    
    onMouseDown: Ext.emptyFn,
    
    onMouseUp: Ext.emptyFn,
    
    setXConstraint: Ext.emptyFn,
    
    setYConstraint: Ext.emptyFn,
    
    resetConstraints: Ext.emptyFn,
    
    clearConstraints: Ext.emptyFn,
    
    clearTicks: Ext.emptyFn,
    
    setInitPosition: Ext.emptyFn,
    
    setDragElId: Ext.emptyFn,
    
    setHandleElId: Ext.emptyFn,
    
    setOuterHandleElId: Ext.emptyFn,
    
    addInvalidHandleClass: Ext.emptyFn,
    
    addInvalidHandleId: Ext.emptyFn,
    
    addInvalidHandleType: Ext.emptyFn,
    
    removeInvalidHandleClass: Ext.emptyFn,
    
    removeInvalidHandleId: Ext.emptyFn,
    
    removeInvalidHandleType: Ext.emptyFn,

    toString: function() {
        return ("DDTarget " + this.id);
    }
});


Ext.define('Ext.dd.DragTracker', {

                              

    mixins: {
        observable:  Ext.util.Observable 
    },

    
    active: false,

    

    
    trackOver: false,

    

    

    
    tolerance: 5,

    
    autoStart: false,

    

    

    

    constructor : function(config){
        var me = this;
        Ext.apply(me, config);
        me.addEvents(
            
            'mouseover',

            
            'mouseout',

            
            'mousedown',

            
            'mouseup',

            
            'mousemove',

            
            'beforedragstart',

            
            'dragstart',

            
            'dragend',

            
            'drag'
        );

        me.dragRegion = new Ext.util.Region(0,0,0,0);

        if (me.el) {
            me.initEl(me.el);
        }

        
        me.mixins.observable.constructor.call(me);
        if (me.disabled) {
            me.disable();
        }

    },

    
    initEl: function(el) {
        var me = this;

        me.el = Ext.get(el);

        
        me.handle = Ext.get(me.delegate);

        
        me.delegate = me.handle ? undefined : me.delegate;

        if (!me.handle) {
            me.handle = me.el;
        }

        
        
        me.handleListeners = {
            scope: me,
            delegate: me.delegate,
            mousedown: me.onMouseDown
        };

        
        
        
        if (me.trackOver || me.overCls) {
            Ext.apply(me.handleListeners, {
                mouseover: me.onMouseOver,
                mouseout: me.onMouseOut
            });
        }
        me.mon(me.handle, me.handleListeners);
    },

    disable: function() {
        this.disabled = true;
    },

    enable: function() {
        this.disabled = false;
    },

    destroy : function() {
        var me = this;

        if (me.active) {
            
            me.endDrag({});
        }
        me.clearListeners();
        me.mun(me.handle, me.handleListeners);
        me.el = me.handle = null;
    },

    
    
    onMouseOver: function(e, target) {
        var me = this;
        if (!me.disabled) {
            if (Ext.EventManager.contains(e) || me.delegate) {
                me.mouseIsOut = false;
                if (me.overCls) {
                    me.el.addCls(me.overCls);
                }
                me.fireEvent('mouseover', me, e, me.delegate ? e.getTarget(me.delegate, target) : me.handle);
            }
        }
    },

    
    
    onMouseOut: function(e) {
        var me = this;

        if (me.mouseIsDown) {
            me.mouseIsOut = true;
        } else {
            if (me.overCls) {
                me.el.removeCls(me.overCls);
            }
            me.fireEvent('mouseout', me, e);
        }
    },

    onMouseDown: function(e, target){
        var me = this,
            el;

        
        if (me.disabled ||e.dragTracked) {
            return;
        }

        
        me.dragTarget = me.delegate ? target : me.handle.dom;
        me.startXY = me.lastXY = e.getXY();
        me.startRegion = Ext.fly(me.dragTarget).getRegion();

        if (me.fireEvent('mousedown', me, e) === false ||
            me.fireEvent('beforedragstart', me, e) === false ||
            me.onBeforeStart(e) === false) {
            return;
        }

        
        
        me.mouseIsDown = true;

        
        e.dragTracked = true;

        
        el = me.el.dom;
        if (Ext.isIE && el.setCapture) {
            el.setCapture();
        }

        if (me.preventDefault !== false) {
            e.preventDefault();
        }
        Ext.getDoc().on({
            scope: me,
            mouseup: me.onMouseUp,
            mousemove: me.onMouseMove,
            selectstart: me.stopSelect
        });
        if (me.autoStart) {
            me.timer =  Ext.defer(me.triggerStart, me.autoStart === true ? 1000 : me.autoStart, me, [e]);
        }
    },

    onMouseMove: function(e, target){
        var me = this,
            xy = e.getXY(),
            s = me.startXY;

        e.preventDefault();

        me.lastXY = xy;
        if (!me.active) {
            if (Math.max(Math.abs(s[0]-xy[0]), Math.abs(s[1]-xy[1])) > me.tolerance) {
                me.triggerStart(e);
            } else {
                return;
            }
        }

        
        if (me.fireEvent('mousemove', me, e) === false) {
            me.onMouseUp(e);
        } else {
            me.onDrag(e);
            me.fireEvent('drag', me, e);
        }
    },

    onMouseUp: function(e) {
        var me = this;
        
        
        me.mouseIsDown = false;

        
        if (me.mouseIsOut) {
            me.mouseIsOut = false;
            me.onMouseOut(e);
        }
        e.preventDefault();

        
        if (Ext.isIE && document.releaseCapture) {
            document.releaseCapture();
        }

        me.fireEvent('mouseup', me, e);
        me.endDrag(e);
    },

    
    endDrag: function(e) {
        var me = this,
            wasActive = me.active;

        Ext.getDoc().un({
            mousemove: me.onMouseMove,
            mouseup: me.onMouseUp,
            selectstart: me.stopSelect,
            scope: me
        });
        me.clearStart();
        me.active = false;
        if (wasActive) {
            me.onEnd(e);
            me.fireEvent('dragend', me, e);
        }
        
        
        me._constrainRegion = Ext.EventObject.dragTracked = null
    },

    triggerStart: function(e) {
        var me = this;
        me.clearStart();
        me.active = true;
        me.onStart(e);
        me.fireEvent('dragstart', me, e);
    },

    clearStart : function() {
        var timer = this.timer;
        if (timer) {
            clearTimeout(timer);
            this.timer = null;
        }
    },

    stopSelect : function(e) {
        e.stopEvent();
        return false;
    },

    
    onBeforeStart : function(e) {

    },

    
    onStart : function(xy) {

    },

    
    onDrag : function(e) {

    },

    
    onEnd : function(e) {

    },

    
    getDragTarget : function(){
        return this.dragTarget;
    },

    
    getDragCt : function(){
        return this.el;
    },

    
    getConstrainRegion: function() {
        var me = this;

        if (me.constrainTo) {
            if (me.constrainTo instanceof Ext.util.Region) {
                return me.constrainTo;
            }
            if (!me._constrainRegion) {
                me._constrainRegion = Ext.fly(me.constrainTo).getViewRegion();
            }
        } else {
            if (!me._constrainRegion) {
                me._constrainRegion = me.getDragCt().getViewRegion();
            }
        }
        return me._constrainRegion;
    },

    getXY : function(constrain){
        return constrain ? this.constrainModes[constrain](this, this.lastXY) : this.lastXY;
    },

    
    getOffset : function(constrain){
        var xy = this.getXY(constrain),
            s = this.startXY;

        return [xy[0]-s[0], xy[1]-s[1]];
    },

    constrainModes: {
        
        point: function(me, xy) {
            var dr = me.dragRegion,
                constrainTo = me.getConstrainRegion();

            
            if (!constrainTo) {
                return xy;
            }

            dr.x = dr.left = dr[0] = dr.right = xy[0];
            dr.y = dr.top = dr[1] = dr.bottom = xy[1];
            dr.constrainTo(constrainTo);

            return [dr.left, dr.top];
        },

        
        dragTarget: function(me, xy) {
            var s = me.startXY,
                dr = me.startRegion.copy(),
                constrainTo = me.getConstrainRegion(),
                adjust;

            
            if (!constrainTo) {
                return xy;
            }

            
            
            
            dr.translateBy(xy[0]-s[0], xy[1]-s[1]);

            
            if (dr.right > constrainTo.right) {
                xy[0] += adjust = (constrainTo.right - dr.right);    
                dr.left += adjust;
            }
            if (dr.left < constrainTo.left) {
                xy[0] += (constrainTo.left - dr.left);      
            }

            
            if (dr.bottom > constrainTo.bottom) {
                xy[1] += adjust = (constrainTo.bottom - dr.bottom);  
                dr.top += adjust;
            }
            if (dr.top < constrainTo.top) {
                xy[1] += (constrainTo.top - dr.top);        
            }
            return xy;
        }
    }
});


Ext.define('Ext.dd.DragZone', {
    extend:  Ext.dd.DragSource ,

    
    constructor : function(el, config){
        var me = this,
            scroll = me.containerScroll;
        
        me.callParent([el, config]);
        if (scroll) {
            el = me.scrollEl || el;
            el = Ext.get(el);
            if (Ext.isObject(scroll)) {
                el.ddScrollConfig = scroll;
            }
            Ext.dd.ScrollManager.register(el);
        }
    },

    
    
    

    
    getDragData : function(e){
        return Ext.dd.Registry.getHandleFromEvent(e);
    },

    
    onInitDrag : function(x, y){
        this.proxy.update(this.dragData.ddel.cloneNode(true));
        this.onStartDrag(x, y);
        return true;
    },

    
    getRepairXY : function(e){
        return Ext.fly(this.dragData.ddel).getXY();
    },

    destroy : function(){
        this.callParent();
        if (this.containerScroll) {
            Ext.dd.ScrollManager.unregister(this.scrollEl || this.el);
        }
    }
});


Ext.define('Ext.dd.ScrollManager', {
    singleton: true,
               
                                
      

    constructor: function() {
        var ddm = Ext.dd.DragDropManager;
        ddm.fireEvents = Ext.Function.createSequence(ddm.fireEvents, this.onFire, this);
        ddm.stopDrag = Ext.Function.createSequence(ddm.stopDrag, this.onStop, this);
        this.doScroll = Ext.Function.bind(this.doScroll, this);
        this.ddmInstance = ddm;
        this.els = {};
        this.dragEl = null;
        this.proc = {};
    },

    onStop: function(e){
        var sm = Ext.dd.ScrollManager;
        sm.dragEl = null;
        sm.clearProc();
    },

    triggerRefresh: function() {
        if (this.ddmInstance.dragCurrent) {
            this.ddmInstance.refreshCache(this.ddmInstance.dragCurrent.groups);
        }
    },

    doScroll: function() {
        if (this.ddmInstance.dragCurrent) {
            var proc   = this.proc,
                procEl = proc.el,
                ddScrollConfig = proc.el.ddScrollConfig,
                inc =     ddScrollConfig && ddScrollConfig.increment    ? ddScrollConfig.increment : this.increment,
                animate = ddScrollConfig && 'animate' in ddScrollConfig ? ddScrollConfig.animate   : this.animate;

            if (!animate) {
                if (procEl.scroll(proc.dir, inc)) {
                    this.triggerRefresh();
                }
            } else {
                procEl.scroll(proc.dir, inc, true, this.animDuration, this.triggerRefresh);
            }
        }
    },

    clearProc: function() {
        var proc = this.proc;
        if (proc.id) {
            clearInterval(proc.id);
        }
        proc.id = 0;
        proc.el = null;
        proc.dir = "";
    },

    startProc: function(el, dir) {
        this.clearProc();
        this.proc.el = el;
        this.proc.dir = dir;
        var group = el.ddScrollConfig ? el.ddScrollConfig.ddGroup : undefined,
            freq  = (el.ddScrollConfig && el.ddScrollConfig.frequency)
                  ? el.ddScrollConfig.frequency
                  : this.frequency;

        if (group === undefined || this.ddmInstance.dragCurrent.ddGroup == group) {
            this.proc.id = setInterval(this.doScroll, freq);
        }
    },

    onFire: function(e, isDrop) {
        if (isDrop || !this.ddmInstance.dragCurrent) {
            return;
        }
        if (!this.dragEl || this.dragEl != this.ddmInstance.dragCurrent) {
            this.dragEl = this.ddmInstance.dragCurrent;
            
            this.refreshCache();
        }

        var pt = e.getPoint(),
            proc = this.proc,
            els = this.els,
            id, el, r, c;

        for (id in els) {
            el = els[id];
            r = el._region;
            c = el.ddScrollConfig ? el.ddScrollConfig : this;
            if (r && r.contains(pt) && el.isScrollable()) {
                if (r.bottom - pt.y <= c.vthresh) {
                    if(proc.el != el){
                        this.startProc(el, "down");
                    }
                    return;
                }else if (r.right - pt.x <= c.hthresh) {
                    if (proc.el != el) {
                        this.startProc(el, "right");
                    }
                    return;
                } else if(pt.y - r.top <= c.vthresh) {
                    if (proc.el != el) {
                        this.startProc(el, "up");
                    }
                    return;
                } else if(pt.x - r.left <= c.hthresh) {
                    if (proc.el != el) {
                        this.startProc(el, "left");
                    }
                    return;
                }
            }
        }
        this.clearProc();
    },

    
    register : function(el){
        if (Ext.isArray(el)) {
            for(var i = 0, len = el.length; i < len; i++) {
                    this.register(el[i]);
            }
        } else {
            el = Ext.get(el);
            this.els[el.id] = el;
        }
    },

    
    unregister : function(el){
        if(Ext.isArray(el)){
            for (var i = 0, len = el.length; i < len; i++) {
                this.unregister(el[i]);
            }
        }else{
            el = Ext.get(el);
            delete this.els[el.id];
        }
    },

    
    vthresh : 25,

    
    hthresh : 25,

    
    increment : 100,

    
    frequency : 500,

    
    animate: true,

    
    animDuration: 0.4,

    
    ddGroup: undefined,

    
    refreshCache : function(){
        var els = this.els,
            id;
        for (id in els) {
            if(typeof els[id] == 'object'){ 
                els[id]._region = els[id].getRegion();
            }
        }
    }
});


Ext.define('Ext.dd.DropTarget', {
    extend:  Ext.dd.DDTarget ,
                                       

    
    constructor : function(el, config){
        this.el = Ext.get(el);

        Ext.apply(this, config);

        if(this.containerScroll){
            Ext.dd.ScrollManager.register(this.el);
        }

        this.callParent([this.el.dom, this.ddGroup || this.group,
              {isTarget: true}]);
    },

    
    
    
    dropAllowed : Ext.baseCSSPrefix + 'dd-drop-ok',
    
    dropNotAllowed : Ext.baseCSSPrefix + 'dd-drop-nodrop',

    
    isTarget : true,

    
    isNotifyTarget : true,

    
    notifyEnter : function(dd, e, data){
        if(this.overClass){
            this.el.addCls(this.overClass);
        }
        return this.dropAllowed;
    },

    
    notifyOver : function(dd, e, data){
        return this.dropAllowed;
    },

    
    notifyOut : function(dd, e, data){
        if(this.overClass){
            this.el.removeCls(this.overClass);
        }
    },

    
    notifyDrop : function(dd, e, data){
        return false;
    },

    destroy : function(){
        this.callParent();
        if(this.containerScroll){
            Ext.dd.ScrollManager.unregister(this.el);
        }
    }
});


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

    
    unregister : function(el){
        var id = this.getId(el, false),
            data = this.elements[id],
            hs, i, len;
        if(data){
            delete this.elements[id];
            if(data.handles){
                hs = data.handles;
                for (i = 0, len = hs.length; i < len; i++) {
                    delete this.handles[this.getId(hs[i], false)];
                }
            }
        }
    },

    
    getHandle : function(id){
        if(typeof id != "string"){ 
            id = id.id;
        }
        return this.handles[id];
    },

    
    getHandleFromEvent : function(e){
        var t = e.getTarget();
        return t ? this.handles[t.id] : null;
    },

    
    getTarget : function(id){
        if(typeof id != "string"){ 
            id = id.id;
        }
        return this.elements[id];
    },

    
    getTargetFromEvent : function(e){
        var t = e.getTarget();
        return t ? this.elements[t.id] || this.handles[t.id] : null;
    }
});


Ext.define('Ext.dd.DropZone', {
    extend:  Ext.dd.DropTarget ,
                                  

    
    getTargetFromEvent : function(e){
        return Ext.dd.Registry.getTargetFromEvent(e);
    },

    
    onNodeEnter : function(n, dd, e, data){
        
    },

    
    onNodeOver : function(n, dd, e, data){
        return this.dropAllowed;
    },

    
    onNodeOut : function(n, dd, e, data){
        
    },

    
    onNodeDrop : function(n, dd, e, data){
        return false;
    },

    
    onContainerOver : function(dd, e, data){
        return this.dropNotAllowed;
    },

    
    onContainerDrop : function(dd, e, data){
        return false;
    },

    
    notifyEnter : function(dd, e, data){
        return this.dropNotAllowed;
    },

    
    notifyOver : function(dd, e, data){
        var n = this.getTargetFromEvent(e);
        if(!n) { 
            if(this.lastOverNode){
                this.onNodeOut(this.lastOverNode, dd, e, data);
                this.lastOverNode = null;
            }
            return this.onContainerOver(dd, e, data);
        }
        if(this.lastOverNode != n){
            if(this.lastOverNode){
                this.onNodeOut(this.lastOverNode, dd, e, data);
            }
            this.onNodeEnter(n, dd, e, data);
            this.lastOverNode = n;
        }
        return this.onNodeOver(n, dd, e, data);
    },

    
    notifyOut : function(dd, e, data){
        if(this.lastOverNode){
            this.onNodeOut(this.lastOverNode, dd, e, data);
            this.lastOverNode = null;
        }
    },

    
    notifyDrop : function(dd, e, data){
        var me = this,
            n = me.getTargetFromEvent(e),
            result = n ?
                me.onNodeDrop(n, dd, e, data) :
                me.onContainerDrop(dd, e, data);

        
        
        if (me.lastOverNode) {
            me.onNodeOut(me.lastOverNode, dd, e, data);
            me.lastOverNode = null;
        }
        return result;
    },

    
    triggerCacheRefresh : function() {
        Ext.dd.DDM.refreshCache(this.groups);
    }
});


Ext.define('Ext.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) {
        if (!Ext.isEmpty(response.responseText)) {
            if (Ext.isObject(response.responseText)) {
                return response.responseText;
            }

            return Ext.decode(response.responseText);
        }

        return null;
    },

    
    createEvents: function(response) {
        var me = this,
            data = null,
            events = [],
            event, i, len;

        try {
            data = me.parseResponse(response);
        }
        catch (e) {
            event = new Ext.direct.ExceptionEvent({
                data: e,
                xhr: response,
                code: Ext.direct.Manager.exceptions.PARSE,
                message: 'Error parsing json response: \n\n ' + e
            });

            return [event];
        }

        if (Ext.isArray(data)) {
            for (i = 0, len = data.length; i < len; ++i) {
                events.push(me.createEvent(data[i]));
            }
        }
        else if (Ext.isObject(data)) {
            events.push(me.createEvent(data));
        }

        return events;
    },

    
    createEvent: function(response) {
        if (typeof response !== 'object'|| !('type' in response)) {
            return new Ext.direct.ExceptionEvent({
                data: response,
                code: Ext.direct.Manager.exceptions.DATA,
                message: 'Invalid data: event type is not specified'
            });
        }
    
        return Ext.create('direct.' + response.type, response);
    }
});


Ext.define('Ext.direct.PollingProvider', {
    extend:  Ext.direct.JsonProvider ,
    alias:  'direct.pollingprovider',
    
               
                   
                              
      
    
           
                                    
                            
      
    
    
    interval: 3000,

    
    
    

    constructor: function(config) {
        var me = this;
        
        me.callParent(arguments);
        
        me.addEvents(
            
            'beforepoll',
            
            
            'poll'
        );
    },

    
    isConnected: function() {
        return !!this.pollTask;
    },

    
    connect: function() {
        var me = this,
            url = me.url;
        
        if (url && !me.pollTask) {
            me.pollTask = Ext.TaskManager.start({
                run: me.runPoll,
                interval: me.interval,
                scope: me
            });
            
            me.fireEvent('connect', me);
        }
    },

    
    disconnect: function() {
        var me = this;
        
        if (me.pollTask) {
            Ext.TaskManager.stop(me.pollTask);
            delete me.pollTask;
            me.fireEvent('disconnect', me);
        }
    },
    
    
    runPoll: function() {
        var me = this,
            url = me.url;
        
        if (me.fireEvent('beforepoll', me) !== false) {
            if (Ext.isFunction(url)) {
                url(me.baseParams);
            }
            else {
                Ext.Ajax.request({
                    url: url,
                    callback: me.onData,
                    scope: me,
                    params: me.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);
        }
    }
});


Ext.define('Ext.direct.RemotingMethod', {

    constructor: function(config) {
        var me = this,
            params = Ext.isDefined(config.params) ? config.params : config.len,
            name, pLen, p, param;

        me.name = config.name;
        me.formHandler = config.formHandler;

        if (Ext.isNumeric(params)) {
            
            me.len = params;
            me.ordered = true;
        }
        else {
            
            me.params = {};
			pLen = params.length;

            for (p = 0; p < pLen; p++) {
                param = params[p];
                name  = Ext.isObject(param) ? param.name : param;
                me.params[name] = true;
            }
        }
    },
    
    getArgs: function(params, paramOrder, paramsAsHash) {
        var me = this,
            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);
        } 
        
        return args;
    },

    
    getCallData: function(args) {
        var me = this,
            data = null,
            len  = me.len,
            params = me.params,
            callback, scope, name, options;

        if (me.ordered) {
            callback = args[len];
            scope    = args[len + 1];
            options  = args[len + 2];
            
            if (len !== 0) {
                data = args.slice(0, len);
            }
        }
        else {
            data     = Ext.apply({}, args[0]);
            callback = args[1];
            scope    = args[2];
            options  = args[3];

            
            for (name in data) {
                if (data.hasOwnProperty(name) && !params[name]) {
                    delete data[name];
                }
            }
        }

        return {
            data: data,
            callback: callback,
            scope: scope,
            options: options
        };
    }
});


Ext.define('Ext.direct.Transaction', {
    alias: 'direct.transaction',
    alternateClassName: 'Ext.Direct.Transaction',
   
    statics: {
        TRANSACTION_ID: 0
    },
    
    
   
    
    constructor: function(config) {
        var me = this;
        
        Ext.apply(me, config);

        me.id = me.tid = ++me.self.TRANSACTION_ID;
        me.retryCount = 0;
    },
   
    send: function() {
        var me = this;
        
        me.provider.queueTransaction(me);
    },

    retry: function() {
        var me = this;
        
        me.retryCount++;
        me.send();
    },

    getProvider: function() {
        return this.provider;
    }
});


Ext.define('Ext.direct.RemotingProvider', {
    extend:  Ext.direct.JsonProvider , 
    alias:  'direct.remotingprovider',
    
               
                                    
                                
                                 
                                   
      
   
   
    
    
    
    
    
    
    
    
    
    
    enableBuffer: 10,
    
    
    maxRetries: 1,
    
    
    
    constructor: function(config) {
        var me = this;

        me.callParent(arguments);

        me.addEvents(
                        
            'beforecall',

                        
            'call',

            
            'beforecallback'
        );

        me.namespace = (Ext.isString(me.namespace)) ? Ext.ns(me.namespace) : me.namespace || Ext.global;
        me.transactions = new Ext.util.MixedCollection();
        me.callBuffer = [];
    },
    
    
    getNamespace: function(root, action) {
        var parts, ns, i, l;
        
        root  = root || Ext.global;
        parts = action.toString().split('.');

        for (i = 0, l = parts.length; i < l; i++) {
            ns   = parts[i];
            root = root[ns];

            if (typeof root === 'undefined') {
                return root;
            }
        }

        return root;
    },

    
    createNamespaces: function(root, action) {
        var parts, ns;
        
        root  = root || Ext.global;
        parts = action.toString().split('.');
        
        for ( var i = 0, l = parts.length; i < l; i++ ) {
            ns = parts[i];
            
            root[ns] = root[ns] || {};
            root     = root[ns];
        };
        
        return root;
    },
    
    
    initAPI: function() {
        var me = this,
            actions = me.actions,
            namespace = me.namespace,
            action, cls, methods, i, len, method;
            
        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] = me.createHandler(action, method);
                }
            }
        }
    },
    
    
    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(form, callback, scope) {
                me.configureFormRequest(action, method, form, callback, scope);
            };
        }

        handler.directCfg = {
            action: action,
            method: method
        };

        return handler;
    },
    
    
    isConnected: function() {
        return !!this.connected;
    },

    
    connect: function() {
        var me = this;
        
        if (me.url) {
            me.initAPI();
            me.connected = true;
            me.fireEvent('connect', me);
        }
    },

    
    disconnect: function() {
        var me = this;
        
        if (me.connected) {
            me.connected = false;
            me.fireEvent('disconnect', me);
        }
    },
    
    
    runCallback: function(transaction, event) {
        var success = !!event.status,
            funcName = success ? 'success' : 'failure',
            callback, options, result;
        
        if (transaction && transaction.callback) {
            callback = transaction.callback;
            options  = transaction.callbackOptions;
            result   = typeof event.result !== 'undefined' ? event.result : event.data;

            if (Ext.isFunction(callback)) {
                callback(result, event, success, options);
            }
            else {
                Ext.callback(callback[funcName], callback.scope, [result, event, success, options]);
                Ext.callback(callback.callback,  callback.scope, [result, event, success, options]);
            }
        }
    },
    
    
    onData: function(options, success, response) {
        var me = this,
            i, len, events, event, transaction, transactions;
            
        if (success) {
            events = me.createEvents(response);

            for (i = 0, len = events.length; i < len; ++i) {
                event = events[i];
                transaction = me.getTransaction(event);
                me.fireEvent('data', me, event);

                if (transaction && me.fireEvent('beforecallback', me, event, transaction) !== false) {
                    me.runCallback(transaction, event, true);
                    Ext.direct.Manager.removeTransaction(transaction);
                }
            }
        }
        else {
            transactions = [].concat(options.transaction);
            
            for (i = 0, len = transactions.length; i < len; ++i) {
                transaction = me.getTransaction(transactions[i]);

                if (transaction && transaction.retryCount < me.maxRetries) {
                    transaction.retry();
                }
                else {
                    event = new Ext.direct.ExceptionEvent({
                        data: null,
                        transaction: transaction,
                        code: Ext.direct.Manager.exceptions.TRANSPORT,
                        message: 'Unable to connect to the server.',
                        xhr: response
                    });

                    me.fireEvent('data', me, event);

                    if (transaction && me.fireEvent('beforecallback', me, transaction) !== false) {
                        me.runCallback(transaction, event, false);
                        Ext.direct.Manager.removeTransaction(transaction);
                    }
                }
            }
        }
    },
    
    
    getTransaction: function(options) {
        return options && options.tid ? Ext.direct.Manager.getTransaction(options.tid) : null;
    },
    
    
    configureRequest: function(action, method, args) {
        var me = this,
            callData, data, callback, scope, opts, transaction, params;

        callData = method.getCallData(args);
        data     = callData.data;
        callback = callData.callback;
        scope    = callData.scope;
        opts     = callData.options || {};

        params = Ext.apply({}, {
            provider: me,
            args: args,
            action: action,
            method: method.name,
            data: data,
            callbackOptions: opts,
            callback: scope && Ext.isFunction(callback) ? Ext.Function.bind(callback, scope) : callback
        });

        if (opts.timeout) {
            Ext.applyIf(params, {
                timeout: opts.timeout
            });
        };

        transaction = new Ext.direct.Transaction(params);

        if (me.fireEvent('beforecall', me, transaction, method) !== false) {
            Ext.direct.Manager.addTransaction(transaction);
            me.queueTransaction(transaction);
            me.fireEvent('call', me, transaction, method);
        }
    },
    
    
    getCallData: function(transaction) {
        return {
            action: transaction.action,
            method: transaction.method,
            data: transaction.data,
            type: 'rpc',
            tid: transaction.id
        };
    },
    
    
    sendRequest: function(data) {
        var me = this,
            request, callData, params,
            enableUrlEncode = me.enableUrlEncode,
            i, len;

        request = {
            url: me.url,
            callback: me.onData,
            scope: me,
            transaction: data,
            timeout: me.timeout
        };

        
        if (data.timeout) {
            request.timeout = data.timeout;
        }

        if (Ext.isArray(data)) {
            callData = [];

            for (i = 0, len = data.length; i < len; ++i) {
                callData.push(me.getCallData(data[i]));
            }
        }
        else {
            callData = me.getCallData(data);
        }

        if (enableUrlEncode) {
            params = {};
            params[Ext.isString(enableUrlEncode) ? enableUrlEncode : 'data'] = Ext.encode(callData);
            request.params = params;
        }
        else {
            request.jsonData = callData;
        }

        Ext.Ajax.request(request);
    },
    
    
    queueTransaction: function(transaction) {
        var me = this,
            enableBuffer = me.enableBuffer;
        
        if (transaction.form) {
            me.sendFormRequest(transaction);
            return;
        }

        if (enableBuffer === false || typeof transaction.timeout !== 'undefined') {
            me.sendRequest(transaction);
            return;
        }
        
        me.callBuffer.push(transaction);

        if (enableBuffer) {
            if (!me.callTask) {
                me.callTask = new Ext.util.DelayedTask(me.combineAndSend, me);
            }

            me.callTask.delay(Ext.isNumber(enableBuffer) ? enableBuffer : 10);
        }
        else {
            me.combineAndSend();
        }
    },
    
    
    combineAndSend : function() {
        var me = this,
            buffer = me.callBuffer,
            len = buffer.length;
            
        if (len > 0) {
            me.sendRequest(len == 1 ? buffer[0] : buffer);
            me.callBuffer = [];
        }
    },
    
    
    configureFormRequest: function(action, method, form, callback, scope) {
        var me = this,
            transaction, isUpload, params;
            
        transaction = new Ext.direct.Transaction({
            provider: me,
            action: action,
            method: method.name,
            args: [form, callback, scope],
            callback: scope && Ext.isFunction(callback) ? Ext.Function.bind(callback, scope) : callback,
            isForm: true
        });

        if (me.fireEvent('beforecall', me, transaction, method) !== false) {
            Ext.direct.Manager.addTransaction(transaction);
            isUpload = String(form.getAttribute("enctype")).toLowerCase() == 'multipart/form-data';
            
            params = {
                extTID: transaction.id,
                extAction: action,
                extMethod: method.name,
                extType: 'rpc',
                extUpload: String(isUpload)
            };
            
            
            
            Ext.apply(transaction, {
                form: Ext.getDom(form),
                isUpload: isUpload,
                params: callback && Ext.isObject(callback.params) ? Ext.apply(params, callback.params) : params
            });

            me.fireEvent('call', me, transaction, method);
            me.sendFormRequest(transaction);
        }
    },
    
    
    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
        });
    }
});


Ext.define('Ext.dom.Layer', {
    extend:  Ext.Element ,
                         
    alternateClassName: 'Ext.Layer',

    

    

    

    

    

    

    

    

    

    

    
    statics: {
        shims: []
    },
    
    isLayer: true,

    localXYNames: {
        get: 'getLocalXY',
        set: 'setLocalXY'
    },

    
    constructor: function(config, existingEl) {
        config = config || {};
        var me = this,
            dh = Ext.DomHelper,
            cp = config.parentEl,
            pel = cp ? Ext.getDom(cp) : document.body,
            hm = config.hideMode,
            cls = Ext.baseCSSPrefix + (config.fixed && !(Ext.isIE6 || Ext.isIEQuirks) ? 'fixed-layer' : 'layer');

        
        
        
        me.el = me;

        if (existingEl) {
            me.dom = Ext.getDom(existingEl);
        }
        if (!me.dom) {
            me.dom = dh.append(pel, config.dh || {
                tag: 'div',
                cls: cls 
            });
        } else {
            me.addCls(cls);
            if (!me.dom.parentNode) {
                pel.appendChild(me.dom);
            }
        }
        
        if (config.preventSync) {
            me.preventSync = true;
        }

        if (config.id) {
            me.id = me.dom.id = config.id;
        } else {
            me.id = Ext.id(me.dom);
        }

        Ext.Element.addToCache(me);

        if (config.cls) {
            me.addCls(config.cls);
        }
        me.constrain = config.constrain !== false;

        
        
        
        if (hm) {
            me.setVisibilityMode(Ext.Element[hm.toUpperCase()]);
            if (me.visibilityMode == Ext.Element.ASCLASS) {
                me.visibilityCls = config.visibilityCls;
            }
        } else if (config.useDisplay) {
            me.setVisibilityMode(Ext.Element.DISPLAY);
        } else {
            me.setVisibilityMode(Ext.Element.VISIBILITY);
        }

        if (config.shadow) {
            me.shadowOffset = config.shadowOffset || 4;
            me.shadow = new Ext.Shadow({
                offset: me.shadowOffset,
                mode: config.shadow,
                fixed: config.fixed
            });
            me.disableShadow();
        } else {
            me.shadowOffset = 0;
        }
        me.useShim = config.shim !== false && Ext.useShims;

        
        
        
        if (config.hidden === true) {
            me.hide();
        } else if (config.hidden === false) {
            me.show();
        }
    },

    getZIndex: function() {
        return parseInt((this.getShim() || this).getStyle('z-index'), 10);
    },

    getShim: function() {
        var me = this,
            shim, pn;

        if (!me.useShim) {
            return null;
        }
        if (!me.shim) {
            shim = me.self.shims.shift();
            if (!shim) {
                shim = me.createShim();
                shim.enableDisplayMode('block');
                shim.hide();
            }
            pn = me.dom.parentNode;
            if (shim.dom.parentNode != pn) {
                pn.insertBefore(shim.dom, me.dom);
            }
            me.shim = shim;
        }
        return me.shim;
    },

    hideShim: function() {
        var me = this;
        
        if (me.shim) {
            me.shim.setDisplayed(false);
            me.self.shims.push(me.shim);
            delete me.shim;
        }
    },

    disableShadow: function() {
        var me = this;
        
        if (me.shadow && !me.shadowDisabled) {
            me.shadowDisabled = true;
            me.shadow.hide();
            me.lastShadowOffset = me.shadowOffset;
            me.shadowOffset = 0;
        }
    },

    enableShadow: function(show) {
        var me = this;
        
        if (me.shadow && me.shadowDisabled) {
            me.shadowDisabled = false;
            me.shadowOffset = me.lastShadowOffset;
            delete me.lastShadowOffset;
            if (show) {
                me.sync(true);
            }
        }
    },

    
    sync: function(doShow) {
        var me = this,
            shadow = me.shadow,
            shadowPos, shimStyle, shadowSize,
            shim, xy, x, y, w, h, shimIndex;
            
        if (me.preventSync) {
            return;
        }

        if (!me.updating && me.isVisible() && (shadow || me.useShim)) {
            shim = me.getShim();
            xy = me[me.localXYNames.get]();
            x = xy[0];
            y = xy[1];
            w = me.dom.offsetWidth;
            h = me.dom.offsetHeight;

            if (shadow && !me.shadowDisabled) {
                if (doShow && !shadow.isVisible()) {
                    shadow.show(me);
                } else {
                    shadow.realign(x, y, w, h);
                }
                if (shim) {
                    
                    shimIndex = shim.getStyle('z-index');
                    if (shimIndex > me.zindex) {
                        me.shim.setStyle('z-index', me.zindex - 2);
                    }
                    shim.show();
                    
                    if (shadow.isVisible()) {
                        shadowPos = shadow.el.getXY();
                        shimStyle = shim.dom.style;
                        shadowSize = shadow.el.getSize();
                        if (Ext.supports.CSS3BoxShadow) {
                            shadowSize.height += 6;
                            shadowSize.width += 4;
                            shadowPos[0] -= 2;
                            shadowPos[1] -= 4;
                        }
                        shimStyle.left = (shadowPos[0]) + 'px';
                        shimStyle.top = (shadowPos[1]) + 'px';
                        shimStyle.width = (shadowSize.width) + 'px';
                        shimStyle.height = (shadowSize.height) + 'px';
                    } else {
                        shim.setSize(w, h);
                        shim[me.localXYNames.set](x, y);
                    }
                }
            } else if (shim) {
                
                shimIndex = shim.getStyle('z-index');
                if (shimIndex > me.zindex) {
                    me.shim.setStyle('z-index', me.zindex - 2);
                }
                shim.show();
                shim.setSize(w, h);
                shim[me.localXYNames.set](x, y);
            }
        }
        return me;
    },

    remove: function() {
        this.hideUnders();
        this.callParent();
    },

    
    beginUpdate: function() {
        this.updating = true;
    },

    
    endUpdate: function() {
        this.updating = false;
        this.sync(true);
    },

    
    hideUnders: function() {
        if (this.shadow) {
            this.shadow.hide();
        }
        this.hideShim();
    },

    
    constrainXY: function() {
        if (this.constrain) {
            var vw = Ext.Element.getViewWidth(),
                vh = Ext.Element.getViewHeight(),
                s = Ext.getDoc().getScroll(),
                xy = this.getXY(),
                x = xy[0],
                y = xy[1],
                so = this.shadowOffset,
                w = this.dom.offsetWidth + so,
                h = this.dom.offsetHeight + so,
                moved = false; 
            
            if ((x + w) > vw + s.left) {
                x = vw - w - so;
                moved = true;
            }
            if ((y + h) > vh + s.top) {
                y = vh - h - so;
                moved = true;
            }
            
            if (x < s.left) {
                x = s.left;
                moved = true;
            }
            if (y < s.top) {
                y = s.top;
                moved = true;
            }
            if (moved) {
                Ext.Layer.superclass.setXY.call(this, [x, y]);
                this.sync();
            }
        }
        return this;
    },

    getConstrainOffset: function() {
        return this.shadowOffset;
    },

    
    setVisible: function(visible, animate, duration, callback, easing) {
        var me = this,
            cb;

        
        cb = function() {
            if (visible) {
                me.sync(true);
            }
            if (callback) {
                callback();
            }
        };

        
        if (!visible) {
            me.hideUnders(true);
        }
        me.callParent([visible, animate, duration, callback, easing]);
        if (!animate) {
            cb();
        }
        return me;
    },

    
    beforeFx: function() {
        this.beforeAction();
        return this.callParent(arguments);
    },

    
    afterFx: function() {
        this.callParent(arguments);
        this.sync(this.isVisible());
    },

    
    beforeAction: function() {
        if (!this.updating && this.shadow) {
            this.shadow.hide();
        }
    },

    
    setLeft: function(left) {
        this.callParent(arguments);
        return this.sync();
    },

    setTop: function(top) {
        this.callParent(arguments);
        return this.sync();
    },

    setLeftTop: function(left, top) {
        this.callParent(arguments);
        return this.sync();
    },

    setLocalX: function() {
        this.callParent(arguments);
        return this.sync();
    },

    setLocalXY: function() {
        this.callParent(arguments);
        return this.sync();
    },

    setLocalY: function() {
        this.callParent(arguments);
        return this.sync();
    },

    setXY: function(xy, animate, duration, callback, easing) {
        var me = this;
        
        
        callback = me.createCB(callback);

        me.fixDisplay();
        me.beforeAction();
        me.callParent([xy, animate, duration, callback, easing]);
        if (!animate) {
            callback();
        }
        return me;
    },

    
    createCB: function(callback) {
        var me = this,
            showShadow = me.shadow && me.shadow.isVisible();

        return function() {
            me.constrainXY();
            me.sync(showShadow);
            if (callback) {
                callback();
            }
        };
    },

    
    setX: function(x, animate, duration, callback, easing) {
        this.setXY([x, this.getY()], animate, duration, callback, easing);
        return this;
    },

    
    setY: function(y, animate, duration, callback, easing) {
        this.setXY([this.getX(), y], animate, duration, callback, easing);
        return this;
    },

    
    setSize: function(w, h, animate, duration, callback, easing) {
        var me = this;
        
        
        callback = me.createCB(callback);

        me.beforeAction();
        me.callParent([w, h, animate, duration, callback, easing]);
        if (!animate) {
            callback();
        }
        return me;
    },

    
    setWidth: function(w, animate, duration, callback, easing) {
        var me = this;
        
        
        callback = me.createCB(callback);

        me.beforeAction();
        me.callParent([w, animate, duration, callback, easing]);
        if (!animate) {
            callback();
        }
        return me;
    },

    
    setHeight: function(h, animate, duration, callback, easing) {
        var me = this;
        
        
        callback = me.createCB(callback);

        me.beforeAction();
        me.callParent([h, animate, duration, callback, easing]);
        if (!animate) {
            callback();
        }
        return me;
    },

    
    setBounds: function(x, y, width, height, animate, duration, callback, easing) {
        var me = this;
        
        
        callback = me.createCB(callback);

        me.beforeAction();
        if (!animate) {
            Ext.Layer.superclass.setXY.call(me, [x, y]);
            Ext.Layer.superclass.setSize.call(me, width, height);
            callback();
        } else {
            me.callParent([x, y, width, height, animate, duration, callback, easing]);
        }
        return me;
    },

    
    setZIndex: function(zindex) {
        var me = this;
        
        me.zindex = zindex;
        if (me.getShim()) {
            me.shim.setStyle('z-index', zindex++);
        }
        if (me.shadow) {
            me.shadow.setZIndex(zindex++);
        }
        return me.setStyle('z-index', zindex);
    },
    
    onOpacitySet: function(opacity){
        var shadow = this.shadow;
        if (shadow) {
            shadow.setOpacity(opacity);
        }
    }
});


Ext.define('Ext.draw.Matrix', {

    

                                

    

    constructor: function(a, b, c, d, e, f) {
        if (a != null) {
            this.matrix = [[a, c, e], [b, d, f], [0, 0, 1]];
        }
        else {
            this.matrix = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
        }
    },

    add: function(a, b, c, d, e, f) {
        var me = this,
            out = [[], [], []],
            matrix = [[a, c, e], [b, d, f], [0, 0, 1]],
            x,
            y,
            z,
            res;

        for (x = 0; x < 3; x++) {
            for (y = 0; y < 3; y++) {
                res = 0;
                for (z = 0; z < 3; z++) {
                    res += me.matrix[x][z] * matrix[z][y];
                }
                out[x][y] = res;
            }
        }
        me.matrix = out;
    },

    prepend: function(a, b, c, d, e, f) {
        var me = this,
            out = [[], [], []],
            matrix = [[a, c, e], [b, d, f], [0, 0, 1]],
            x,
            y,
            z,
            res;

        for (x = 0; x < 3; x++) {
            for (y = 0; y < 3; y++) {
                res = 0;
                for (z = 0; z < 3; z++) {
                    res += matrix[x][z] * me.matrix[z][y];
                }
                out[x][y] = res;
            }
        }
        me.matrix = out;
    },

    invert: function() {
        var matrix = this.matrix,
            a = matrix[0][0],
            b = matrix[1][0],
            c = matrix[0][1],
            d = matrix[1][1],
            e = matrix[0][2],
            f = matrix[1][2],
            x = a * d - b * c;
        return new Ext.draw.Matrix(d / x, -b / x, -c / x, a / x, (c * f - d * e) / x, (b * e - a * f) / x);
    },

    clone: function() {
        var matrix = this.matrix,
            a = matrix[0][0],
            b = matrix[1][0],
            c = matrix[0][1],
            d = matrix[1][1],
            e = matrix[0][2],
            f = matrix[1][2];
        return new Ext.draw.Matrix(a, b, c, d, e, f);
    },

    translate: function(x, y) {
        this.prepend(1, 0, 0, 1, x, y);
    },

    scale: function(x, y, cx, cy) {
        var me = this;
        if (y == null) {
            y = x;
        }
        me.add(x, 0, 0, y, cx * (1 - x), cy * (1 - y));
    },

    rotate: function(a, x, y) {
        a = Ext.draw.Draw.rad(a);
        var me = this,
            cos = +Math.cos(a).toFixed(9),
            sin = +Math.sin(a).toFixed(9);
        me.add(cos, sin, -sin, cos, x - cos * x + sin * y, -(sin * x) + y - cos * y);
    },

    x: function(x, y) {
        var matrix = this.matrix;
        return x * matrix[0][0] + y * matrix[0][1] + matrix[0][2];
    },

    y: function(x, y) {
        var matrix = this.matrix;
        return x * matrix[1][0] + y * matrix[1][1] + matrix[1][2];
    },

    get: function(i, j) {
        return + this.matrix[i][j].toFixed(4);
    },

    toString: function() {
        var me = this;
        return [me.get(0, 0), me.get(0, 1), me.get(1, 0), me.get(1, 1), 0, 0].join();
    },

    toSvg: function() {
        var me = this;
        return "matrix(" + [me.get(0, 0), me.get(1, 0), me.get(0, 1), me.get(1, 1), me.get(0, 2), me.get(1, 2)].join() + ")";
    },

    toFilter: function(dx, dy) {
        var me = this;
        dx = dx || 0;
        dy = dy || 0;
        return "progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand', filterType='bilinear', M11=" + me.get(0, 0) +
            ", M12=" + me.get(0, 1) + ", M21=" + me.get(1, 0) + ", M22=" + me.get(1, 1) +
            ", Dx=" + (me.get(0, 2) + dx) + ", Dy=" + (me.get(1, 2) + dy) + ")";
    },

    offset: function() {
        var matrix = this.matrix;
        return [(matrix[0][2] || 0).toFixed(4), (matrix[1][2] || 0).toFixed(4)];
    },

    
    split: function () {
        function norm(a) {
            return a[0] * a[0] + a[1] * a[1];
        }
        function normalize(a) {
            var mag = Math.sqrt(norm(a));
            a[0] /= mag;
            a[1] /= mag;
        }
        var matrix = this.matrix,
            out = {
                translateX: matrix[0][2],
                translateY: matrix[1][2]
            },
            row;

        
        row = [[matrix[0][0], matrix[0][1]], [matrix[1][1], matrix[1][1]]];
        out.scaleX = Math.sqrt(norm(row[0]));
        normalize(row[0]);

        out.shear = row[0][0] * row[1][0] + row[0][1] * row[1][1];
        row[1] = [row[1][0] - row[0][0] * out.shear, row[1][1] - row[0][1] * out.shear];

        out.scaleY = Math.sqrt(norm(row[1]));
        normalize(row[1]);
        out.shear /= out.scaleY;

        
        out.rotate = Math.asin(-row[0][1]);

        out.isSimple = !+out.shear.toFixed(9) && (out.scaleX.toFixed(9) == out.scaleY.toFixed(9) || !out.rotate);

        return out;
    }
});


Ext.define('Ext.draw.SpriteDD', {
    extend:  Ext.dd.DragSource ,

    constructor : function(sprite, cfg){
        var me = this,
            el = sprite.el;
        me.sprite = sprite;
        me.el = el;
        me.dragData = {el: el, sprite: sprite};
        me.callParent([el, cfg]);
        me.sprite.setStyle('cursor', 'move');
    },

    showFrame: Ext.emptyFn,
    createFrame : Ext.emptyFn,

    getDragEl : function(e){
        return this.el;
    },
    
    getRegion: function() {
        var me = this,
            el = me.el,
            pos, x1, x2, y1, y2, t, r, b, l, bbox, sprite;
        
        sprite = me.sprite;
        bbox = sprite.getBBox();
        
        try {
            pos = Ext.Element.getXY(el);
        } catch (e) { }

        if (!pos) {
            return null;
        }

        x1 = pos[0];
        x2 = x1 + bbox.width;
        y1 = pos[1];
        y2 = y1 + bbox.height;
        
        return new Ext.util.Region(y1, x2, y2, x1);
    },

    
     
    startDrag: function(x, y) {
        var me = this,
            attr = me.sprite.attr;
        me.prev = me.sprite.surface.transformToViewBox(x, y);
    },

    onDrag: function(e) {
        var xy = e.getXY(),
            me = this,
            sprite = me.sprite,
            attr = sprite.attr, dx, dy;
        xy = me.sprite.surface.transformToViewBox(xy[0], xy[1]);
        dx = xy[0] - me.prev[0];
        dy = xy[1] - me.prev[1];
        sprite.setAttributes({
            translate: {
                x: attr.translation.x + dx,
                y: attr.translation.y + dy
            }
        }, true);
        me.prev = xy;
    },

    setDragElPos: function () {
        
        return false;
    }
});


Ext.define('Ext.draw.Sprite', {

    

    mixins: {
        observable:  Ext.util.Observable ,
        animate:  Ext.util.Animate 
    },

                                    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    dirty: false,
    dirtyHidden: false,
    dirtyTransform: false,
    dirtyPath: true,
    dirtyFont: true,
    zIndexDirty: true,

    
    isSprite: true,
    zIndex: 0,
    fontProperties: [
        'font',
        'font-size',
        'font-weight',
        'font-style',
        'font-family',
        'text-anchor',
        'text'
    ],
    pathProperties: [
        'x',
        'y',
        'd',
        'path',
        'height',
        'width',
        'radius',
        'r',
        'rx',
        'ry',
        'cx',
        'cy'
    ],
    constructor: function(config) {
        var me = this;
        config = Ext.merge({}, config || {});
        me.id = Ext.id(null, 'ext-sprite-');
        me.transformations = [];
        Ext.copyTo(this, config, 'surface,group,type,draggable');
        
        me.bbox = {};
        me.attr = {
            zIndex: 0,
            translation: {
                x: null,
                y: null
            },
            rotation: {
                degrees: null,
                x: null,
                y: null
            },
            scaling: {
                x: null,
                y: null,
                cx: null,
                cy: null
            }
        };
        
        delete config.surface;
        delete config.group;
        delete config.type;
        delete config.draggable;
        me.setAttributes(config);
        me.addEvents(
            
            'beforedestroy',
            
            'destroy',
            
            'render',
            
            'mousedown',
            
            'mouseup',
            
            'mouseover',
            
            'mouseout',
            
            'mousemove',
            
            'click'
        );
        me.mixins.observable.constructor.apply(this, arguments);
    },

    

    initDraggable: function() {
        var me = this;
        
        if (!me.el) {
            me.surface.createSpriteElement(me);
        }
        me.dd = new Ext.draw.SpriteDD(me, Ext.isBoolean(me.draggable) ? null : me.draggable);
        me.on('beforedestroy', me.dd.destroy, me.dd);
    },

    
    setAttributes: function(attrs, redraw) {
        var me = this,
            fontProps = me.fontProperties,
            fontPropsLength = fontProps.length,
            pathProps = me.pathProperties,
            pathPropsLength = pathProps.length,
            hasSurface = !!me.surface,
            custom = hasSurface && me.surface.customAttributes || {},
            spriteAttrs = me.attr,
            dirtyBBox = false,
            attr, i, newTranslation, translation, newRotate, rotation, newScaling, scaling;

        attrs = Ext.apply({}, attrs);
        for (attr in custom) {
            if (attrs.hasOwnProperty(attr) && typeof custom[attr] == "function") {
                Ext.apply(attrs, custom[attr].apply(me, [].concat(attrs[attr])));
            }
        }

        
        if (!!attrs.hidden !== !!spriteAttrs.hidden) {
            me.dirtyHidden = true;
        }

        
        for (i = 0; i < pathPropsLength; i++) {
            attr = pathProps[i];
            if (attr in attrs && attrs[attr] !== spriteAttrs[attr]) {
                me.dirtyPath = true;
                dirtyBBox = true;
                break;
            }
        }

        
        if ('zIndex' in attrs) {
            me.zIndexDirty = true;
        }

        
        if ('text' in attrs) {
            me.dirtyFont = true;
            dirtyBBox = true;
            attrs.text = me.transformText(attrs.text);
        }

        for (i = 0; i < fontPropsLength; i++) {
            attr = fontProps[i];
            if (attr in attrs && attrs[attr] !== spriteAttrs[attr]) {
                me.dirtyFont = true;
                dirtyBBox = true;
                break;
            }
        }

        newTranslation = attrs.translation || attrs.translate;
        delete attrs.translate;
        delete attrs.translation;
        translation = spriteAttrs.translation;
        if (newTranslation) {
            if (('x' in newTranslation && newTranslation.x !== translation.x) ||
                ('y' in newTranslation && newTranslation.y !== translation.y)) {
                me.dirtyTransform = true;
                translation.x = newTranslation.x;
                translation.y = newTranslation.y;
            }
        }

        newRotate = attrs.rotation || attrs.rotate;
        rotation = spriteAttrs.rotation;
        delete attrs.rotate;
        delete attrs.rotation;
        if (newRotate) {
            if (('x' in newRotate && newRotate.x !== rotation.x) ||
                ('y' in newRotate && newRotate.y !== rotation.y) ||
                ('degrees' in newRotate && newRotate.degrees !== rotation.degrees)) {
                me.dirtyTransform = true;
                rotation.x = newRotate.x;
                rotation.y = newRotate.y;
                rotation.degrees = newRotate.degrees;
            }
        }

        newScaling = attrs.scaling || attrs.scale;
        scaling = spriteAttrs.scaling;
        delete attrs.scale;
        delete attrs.scaling;
        if (newScaling) {
            if (('x' in newScaling && newScaling.x !== scaling.x) ||
                ('y' in newScaling && newScaling.y !== scaling.y) ||
                ('cx' in newScaling && newScaling.cx !== scaling.cx) ||
                ('cy' in newScaling && newScaling.cy !== scaling.cy)) {
                me.dirtyTransform = true;
                scaling.x = newScaling.x;
                scaling.y = newScaling.y;
                scaling.cx = newScaling.cx;
                scaling.cy = newScaling.cy;
            }
        }

        
        if (!me.dirtyTransform && dirtyBBox) {
            if (spriteAttrs.scaling.x === null ||
                spriteAttrs.scaling.y === null ||
                spriteAttrs.rotation.y === null ||
                spriteAttrs.rotation.y === null) {
                me.dirtyTransform = true;
            }
        }

        Ext.apply(spriteAttrs, attrs);
        me.dirty = true;

        if (redraw === true && hasSurface) {
            me.redraw();
        }
        return this;
    },
    
    transformText: Ext.identityFn,

    
    getBBox: function() {
        return this.surface.getBBox(this);
    },

    setText: function(text) {
        return this.surface.setText(this, text);
    },

    
    hide: function(redraw) {
        this.setAttributes({
            hidden: true
        }, redraw);
        return this;
    },

    
    show: function(redraw) {
        this.setAttributes({
            hidden: false
        }, redraw);
        return this;
    },

    
    remove: function() {
        if (this.surface) {
            this.surface.remove(this);
            return true;
        }
        return false;
    },

    onRemove: function() {
        this.surface.onRemove(this);
    },

    
    destroy: function() {
        var me = this;
        if (me.fireEvent('beforedestroy', me) !== false) {
            me.remove();
            me.surface.onDestroy(me);
            me.clearListeners();
            me.fireEvent('destroy');
        }
    },

    
    redraw: function() {
        var me = this,
            changed = !me.el || me.dirty,
            surface = me.surface,
            owner;
            
        surface.renderItem(me);
        
        
        
        if (changed) {
            owner = surface.owner;
            if (!me.isBackground && owner && (owner.viewBox || owner.autoSize)) {
                owner.configureSurfaceSize();
            }
        }
        return this;
    },

    
    setStyle: function() {
        this.el.setStyle.apply(this.el, arguments);
        return this;
    },

    
    addCls: function(obj) {
        this.surface.addCls(this, obj);
        return this;
    },

    
    removeCls: function(obj) {
        this.surface.removeCls(this, obj);
        return this;
    }
});


Ext.define('Ext.draw.Text', {
    extend:  Ext.draw.Component ,
                           
    alias: 'widget.text',

    
    text: '',

    

    

    focusable: false,
    viewBox: false,
    autoSize: true,
    baseCls: Ext.baseCSSPrefix + 'surface ' + Ext.baseCSSPrefix + 'draw-text',

    initComponent: function() {
        var me = this;

        me.textConfig = Ext.apply({
            type: 'text',
            text: me.text,
            rotate: {
                degrees: me.degrees || 0
            }
        }, me.textStyle);
        Ext.apply(me.textConfig, me.getStyles(me.styleSelectors || me.styleSelector));

        
        
        me.initialConfig.items = [me.textConfig];
        me.callParent(arguments);
    },

    
    getStyles: function(selectors) {
        selectors = Ext.Array.from(selectors);
        var i = 0,
            len = selectors.length,
            rule,
            style,
            prop,
            result = {};

        for (; i < len; i++) {
            
            rule = Ext.util.CSS.getRule(selectors[i]);
            if (rule) {
                style = rule.style;
                if (style) {
                    Ext.apply(result, {
                        'font-family': style.fontFamily,
                        'font-weight': style.fontWeight,
                        'line-height': style.lineHeight,
                        'font-size': style.fontSize,
                        fill: style.color
                    });
                }
            }
        }
        return result;
    },

    
    setAngle: function(degrees) {
        var me = this,
            surface,
            sprite;
            
        if (me.rendered) {
            surface = me.surface;
            sprite = surface.items.items[0];

            me.degrees = degrees;
            sprite.setAttributes({
                rotate: {
                    degrees: degrees
                }
            }, true);
            if (me.autoSize || me.viewBox) {
                me.updateLayout();
            }
        } else {
            me.degrees = degrees;
        }
    },

    
    setText: function(text) {
        var me = this,
            surface,
            sprite;
            
        if (me.rendered) {
            surface = me.surface;
            sprite = surface.items.items[0];

            me.text = text || '';
            surface.remove(sprite);
            me.textConfig.type = 'text';
            me.textConfig.text = me.text;
            sprite = surface.add(me.textConfig);
            sprite.setAttributes({
                rotate: {
                    degrees: me.degrees
                }
            }, true);
            if (me.autoSize || me.viewBox) {
                me.updateLayout();
            }
        } else {
            me.on({
                render: function() {
                    me.setText(text);
                },
                single: true
            });
        }
    }
});


Ext.define('Ext.draw.engine.ImageExporter', {
    singleton: true,

    
    defaultUrl: 'http://svg.sencha.io',

    
    supportedTypes: ['image/png', 'image/jpeg'],

    
    widthParam: 'width',

    
    heightParam: 'height',

    
    typeParam: 'type',

    
    svgParam: 'svg',

    formCls: Ext.baseCSSPrefix + 'hide-display',

    
    generate: function(surface, config) {
        config = config || {};
        var me = this,
            type = config.type,
            form;

        if (Ext.Array.indexOf(me.supportedTypes, type) === -1) {
            return false;
        }

        form = Ext.getBody().createChild({
            tag: 'form',
            method: 'POST',
            action: config.url || me.defaultUrl,
            cls: me.formCls,
            children: [{
                tag: 'input',
                type: 'hidden',
                name: config.widthParam || me.widthParam,
                value: config.width || surface.width
            }, {
                tag: 'input',
                type: 'hidden',
                name: config.heightParam || me.heightParam,
                value: config.height || surface.height
            }, {
                tag: 'input',
                type: 'hidden',
                name: config.typeParam || me.typeParam,
                value: type
            }, {
                tag: 'input',
                type: 'hidden',
                name: config.svgParam || me.svgParam
            }]
        });

        
        form.last(null, true).value = Ext.draw.engine.SvgExporter.generate(surface);

        form.dom.submit();
        form.remove();
        return true;
    }

});


Ext.define('Ext.draw.engine.Svg', {

    

    extend:  Ext.draw.Surface ,

                                                                                     

    

    engine: 'Svg',

    trimRe: /^\s+|\s+$/g,
    spacesRe: /\s+/,
    xlink: "http:/" + "/www.w3.org/1999/xlink",

    translateAttrs: {
        radius: "r",
        radiusX: "rx",
        radiusY: "ry",
        path: "d",
        lineWidth: "stroke-width",
        fillOpacity: "fill-opacity",
        strokeOpacity: "stroke-opacity",
        strokeLinejoin: "stroke-linejoin"
    },

    parsers: {},

    minDefaults: {
        circle: {
            cx: 0,
            cy: 0,
            r: 0,
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        ellipse: {
            cx: 0,
            cy: 0,
            rx: 0,
            ry: 0,
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        rect: {
            x: 0,
            y: 0,
            width: 0,
            height: 0,
            rx: 0,
            ry: 0,
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        text: {
            x: 0,
            y: 0,
            "text-anchor": "start",
            "font-family": null,
            "font-size": null,
            "font-weight": null,
            "font-style": null,
            fill: "#000",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        path: {
            d: "M0,0",
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        image: {
            x: 0,
            y: 0,
            width: 0,
            height: 0,
            preserveAspectRatio: "none",
            opacity: null
        }
    },

    createSvgElement: function(type, attrs) {
        var el = this.domRef.createElementNS("http:/" + "/www.w3.org/2000/svg", type),
            key;
        if (attrs) {
            for (key in attrs) {
                el.setAttribute(key, String(attrs[key]));
            }
        }
        return el;
    },

    createSpriteElement: function(sprite) {
        
        var el = this.createSvgElement(sprite.type);
        el.id = sprite.id;
        if (el.style) {
            el.style.webkitTapHighlightColor = "rgba(0,0,0,0)";
        }
        sprite.el = Ext.get(el);
        this.applyZIndex(sprite); 
        sprite.matrix = new Ext.draw.Matrix();
        sprite.bbox = {
            plain: 0,
            transform: 0
        };
        this.applyAttrs(sprite);
        this.applyTransformations(sprite);
        sprite.fireEvent("render", sprite);
        return el;
    },
    
    getBBoxText: function (sprite) {
        var bbox = {},
            bb, height, width, i, ln, el;

        if (sprite && sprite.el) {
            el = sprite.el.dom;
            try {
                bbox = el.getBBox();
                return bbox;
            } catch(e) {
                
            }
            bbox = {x: bbox.x, y: Infinity, width: 0, height: 0};
            ln = el.getNumberOfChars();
            for (i = 0; i < ln; i++) {
                bb = el.getExtentOfChar(i);
                bbox.y = Math.min(bb.y, bbox.y);
                height = bb.y + bb.height - bbox.y;
                bbox.height = Math.max(bbox.height, height);
                width = bb.x + bb.width - bbox.x;
                bbox.width = Math.max(bbox.width, width);
            }
            return bbox;
        }
    },

    hide: function() {
        Ext.get(this.el).hide();
    },

    show: function() {
        Ext.get(this.el).show();
    },

    hidePrim: function(sprite) {
        this.addCls(sprite, Ext.baseCSSPrefix + 'hide-visibility');
    },

    showPrim: function(sprite) {
        this.removeCls(sprite, Ext.baseCSSPrefix + 'hide-visibility');
    },

    getDefs: function() {
        return this._defs || (this._defs = this.createSvgElement("defs"));
    },

    transform: function(sprite, matrixOnly) {
        var me = this,
            matrix = new Ext.draw.Matrix(),
            transforms = sprite.transformations,
            transformsLength = transforms.length,
            i = 0,
            transform, type;
            
        for (; i < transformsLength; i++) {
            transform = transforms[i];
            type = transform.type;
            if (type == "translate") {
                matrix.translate(transform.x, transform.y);
            }
            else if (type == "rotate") {
                matrix.rotate(transform.degrees, transform.x, transform.y);
            }
            else if (type == "scale") {
                matrix.scale(transform.x, transform.y, transform.centerX, transform.centerY);
            }
        }
        sprite.matrix = matrix;
        if (!matrixOnly) {
            sprite.el.set({transform: matrix.toSvg()});
        }
    },

    setSize: function(width, height) {
        var me = this,
            el = me.el;
        
        width = +width || me.width;
        height = +height || me.height;
        me.width = width;
        me.height = height;

        el.setSize(width, height);
        el.set({
            width: width,
            height: height
        });
        me.callParent([width, height]);
    },

    
    getRegion: function() {
        
        
        var svgXY = this.el.getXY(),
            rectXY = this.bgRect.getXY(),
            max = Math.max,
            x = max(svgXY[0], rectXY[0]),
            y = max(svgXY[1], rectXY[1]);
        return {
            left: x,
            top: y,
            right: x + this.width,
            bottom: y + this.height
        };
    },

    onRemove: function(sprite) {
        if (sprite.el) {
            sprite.el.destroy();
            delete sprite.el;
        }
        this.callParent(arguments);
    },
    
    setViewBox: function(x, y, width, height) {
        if (isFinite(x) && isFinite(y) && isFinite(width) && isFinite(height)) {
            this.callParent(arguments);
            this.el.dom.setAttribute("viewBox", [x, y, width, height].join(" "));
        }
    },

    render: function (container) {
        var me = this,
            el, defs, bgRect, webkitRect;
            
        if (!me.el) {
            cfg = {
                xmlns: "http:/" + "/www.w3.org/2000/svg",
                version: 1.1,
                width: me.width || 0,
                height: me.height || 0
            };
            
            if (me.forceLtr) {
                cfg.direction = 'ltr';
            }
            
            el = me.createSvgElement('svg', cfg);
            defs = me.getDefs();

            
            
            
            
            bgRect = me.createSvgElement("rect", {
                width: "100%",
                height: "100%",
                fill: "#000",
                stroke: "none",
                opacity: 0
            });
            
            if (Ext.isSafari3) {
                
                webkitRect = me.createSvgElement("rect", {
                    x: -10,
                    y: -10,
                    width: "110%",
                    height: "110%",
                    fill: "none",
                    stroke: "#000"
                });
            }
            el.appendChild(defs);
            if (Ext.isSafari3) {
                el.appendChild(webkitRect);
            }
            el.appendChild(bgRect);
            container.appendChild(el);
            me.el = Ext.get(el);
            me.bgRect = Ext.get(bgRect);
            if (Ext.isSafari3) {
                me.webkitRect = Ext.get(webkitRect);
                me.webkitRect.hide();
            }
            me.el.on({
                scope: me,
                mouseup: me.onMouseUp,
                mousedown: me.onMouseDown,
                mouseover: me.onMouseOver,
                mouseout: me.onMouseOut,
                mousemove: me.onMouseMove,
                mouseenter: me.onMouseEnter,
                mouseleave: me.onMouseLeave,
                click: me.onClick,
                dblclick: me.onDblClick
            });
        }
        me.renderAll();
    },

    
    onMouseEnter: function(e) {
        if (this.el.parent().getRegion().contains(e.getPoint())) {
            this.fireEvent('mouseenter', e);
        }
    },

    
    onMouseLeave: function(e) {
        if (!this.el.parent().getRegion().contains(e.getPoint())) {
            this.fireEvent('mouseleave', e);
        }
    },
    
    processEvent: function(name, e) {
        var target = e.getTarget(),
            surface = this.surface,
            sprite;

        this.fireEvent(name, e);
        
        if (target.nodeName == "tspan" && target.parentNode) {
            target = target.parentNode;
        }
        sprite = this.items.get(target.id);
        if (sprite) {
            sprite.fireEvent(name, sprite, e);
        }
    },

    
    tuneText: function (sprite, attrs) {
        var el = sprite.el.dom,
            tspans = [],
            height, tspan, text, i, ln, texts, factor, x;

        if (attrs.hasOwnProperty("text")) {
            
            
            

            
            text = sprite.tspans && Ext.Array.map(sprite.tspans, function(t) { return t.textContent; }).join('');

            if (!sprite.tspans || attrs.text != text) {
                tspans = this.setText(sprite, attrs.text);
                sprite.tspans = tspans;
            
            } else {
                tspans = sprite.tspans || [];
            }
        }
        
        if (tspans.length) {
            height = this.getBBoxText(sprite).height;
            x = sprite.el.dom.getAttribute("x");
            for (i = 0, ln = tspans.length; i < ln; i++) {
                
                
                factor = (Ext.isFF3_0 || Ext.isFF3_5) ? 2 : 4;
                tspans[i].setAttribute("x", x);
                tspans[i].setAttribute("dy", i ? height * 1.2 : height / factor);
            }
            sprite.dirty = true;
        }
    },

    setText: function(sprite, textString) {
         var me = this,
             el = sprite.el.dom,
             tspans = [],
             height, tspan, text, i, ln, texts;
        
        while (el.firstChild) {
            el.removeChild(el.firstChild);
        }
        
        texts = String(textString).split("\n");
        for (i = 0, ln = texts.length; i < ln; i++) {
            text = texts[i];
            if (text) {
                tspan = me.createSvgElement("tspan");
                tspan.appendChild(document.createTextNode(Ext.htmlDecode(text)));
                el.appendChild(tspan);
                tspans[i] = tspan;
            }
        }
        return tspans;
    },

    renderAll: function() {
        this.items.each(this.renderItem, this);
    },

    renderItem: function (sprite) {
        if (!this.el) {
            return;
        }
        if (!sprite.el) {
            this.createSpriteElement(sprite);
        }
        if (sprite.zIndexDirty) {
            this.applyZIndex(sprite);
        }
        if (sprite.dirty) {
            this.applyAttrs(sprite);
            if (sprite.dirtyTransform) {
                this.applyTransformations(sprite);
            }
        }
    },

    redraw: function(sprite) {
        sprite.dirty = sprite.zIndexDirty = true;
        this.renderItem(sprite);
    },

    applyAttrs: function (sprite) {
        var me = this,
            el = sprite.el,
            group = sprite.group,
            sattr = sprite.attr,
            parsers = me.parsers,
            
            
            
            gradientsMap = me.gradientsMap || {},
            safariFix = Ext.isSafari && !Ext.isStrict,
            groups, i, ln, attrs, font, key, style, name, rect;

        if (group) {
            groups = [].concat(group);
            ln = groups.length;
            for (i = 0; i < ln; i++) {
                group = groups[i];
                me.getGroup(group).add(sprite);
            }
            delete sprite.group;
        }
        attrs = me.scrubAttrs(sprite) || {};

        
            sprite.bbox.plain = 0;
            sprite.bbox.transform = 0;
            if (sprite.type == "circle" || sprite.type == "ellipse") {
                attrs.cx = attrs.cx || attrs.x;
                attrs.cy = attrs.cy || attrs.y;
            }
            else if (sprite.type == "rect") {
                attrs.rx = attrs.ry = attrs.r;
            }
            else if (sprite.type == "path" && attrs.d) {
                attrs.d = Ext.draw.Draw.pathToString(Ext.draw.Draw.pathToAbsolute(attrs.d));
            }
            sprite.dirtyPath = false;
        
        
        
        

        if (attrs['clip-rect']) {
            me.setClip(sprite, attrs);
            delete attrs['clip-rect'];
        }
        if (sprite.type == 'text' && attrs.font && sprite.dirtyFont) {
            el.set({ style: "font: " + attrs.font});
        }
        if (sprite.type == "image") {
            el.dom.setAttributeNS(me.xlink, "href", attrs.src);
        }
        Ext.applyIf(attrs, me.minDefaults[sprite.type]);

        if (sprite.dirtyHidden) {
            (sattr.hidden) ? me.hidePrim(sprite) : me.showPrim(sprite);
            sprite.dirtyHidden = false;
        }
        for (key in attrs) {
            if (attrs.hasOwnProperty(key) && attrs[key] != null) {
                
                
                
                
                
                if (safariFix && ('color|stroke|fill'.indexOf(key) > -1) && (attrs[key] in gradientsMap)) {
                    attrs[key] = gradientsMap[attrs[key]];
                }
                
                if (key == 'hidden' && sprite.type == 'text') {
                    continue;
                }
                if (key in parsers) {
                    el.dom.setAttribute(key, parsers[key](attrs[key], sprite, me));
                } else {
                    el.dom.setAttribute(key, attrs[key]);
                }
            }
        }
        
        if (sprite.type == 'text') {
            me.tuneText(sprite, attrs);
        }
        sprite.dirtyFont = false;

        
        style = sattr.style;
        if (style) {
            el.setStyle(style);
        }

        sprite.dirty = false;

        if (Ext.isSafari3) {
            
            me.webkitRect.show();
            setTimeout(function () {
                me.webkitRect.hide();
            });
        }
    },

    setClip: function(sprite, params) {
        var me = this,
            rect = params["clip-rect"],
            clipEl, clipPath;
        if (rect) {
            if (sprite.clip) {
                sprite.clip.parentNode.parentNode.removeChild(sprite.clip.parentNode);
            }
            clipEl = me.createSvgElement('clipPath');
            clipPath = me.createSvgElement('rect');
            clipEl.id = Ext.id(null, 'ext-clip-');
            clipPath.setAttribute("x", rect.x);
            clipPath.setAttribute("y", rect.y);
            clipPath.setAttribute("width", rect.width);
            clipPath.setAttribute("height", rect.height);
            clipEl.appendChild(clipPath);
            me.getDefs().appendChild(clipEl);
            sprite.el.dom.setAttribute("clip-path", "url(#" + clipEl.id + ")");
            sprite.clip = clipPath;
        } 
        
        
        
        
        
        
    },

    
    applyZIndex: function(sprite) {
        var me = this,
            items = me.items,
            idx = items.indexOf(sprite),
            el = sprite.el,
            prevEl;
        if (me.el.dom.childNodes[idx + 2] !== el.dom) { 
            if (idx > 0) {
                
                do {
                    prevEl = items.getAt(--idx).el;
                } while (!prevEl && idx > 0);
            }
            el.insertAfter(prevEl || me.bgRect);
        }
        sprite.zIndexDirty = false;
    },

    createItem: function (config) {
        var sprite = new Ext.draw.Sprite(config);
        sprite.surface = this;
        return sprite;
    },

    addGradient: function(gradient) {
        gradient = Ext.draw.Draw.parseGradient(gradient);
        var me = this,
            ln = gradient.stops.length,
            vector = gradient.vector,
            
            
            
            usePlain = Ext.isSafari && !Ext.isStrict,
            gradientEl, stop, stopEl, i, gradientsMap;
            
        gradientsMap = me.gradientsMap || {};
        
        if (!usePlain) {
            if (gradient.type == "linear") {
                gradientEl = me.createSvgElement("linearGradient");
                gradientEl.setAttribute("x1", vector[0]);
                gradientEl.setAttribute("y1", vector[1]);
                gradientEl.setAttribute("x2", vector[2]);
                gradientEl.setAttribute("y2", vector[3]);
            }
            else {
                gradientEl = me.createSvgElement("radialGradient");
                gradientEl.setAttribute("cx", gradient.centerX);
                gradientEl.setAttribute("cy", gradient.centerY);
                gradientEl.setAttribute("r", gradient.radius);
                if (Ext.isNumber(gradient.focalX) && Ext.isNumber(gradient.focalY)) {
                    gradientEl.setAttribute("fx", gradient.focalX);
                    gradientEl.setAttribute("fy", gradient.focalY);
                }
            }
            gradientEl.id = gradient.id;
            me.getDefs().appendChild(gradientEl);
            for (i = 0; i < ln; i++) {
                stop = gradient.stops[i];
                stopEl = me.createSvgElement("stop");
                stopEl.setAttribute("offset", stop.offset + "%");
                stopEl.setAttribute("stop-color", stop.color);
                stopEl.setAttribute("stop-opacity",stop.opacity);
                gradientEl.appendChild(stopEl);
            }
        } else {
            gradientsMap['url(#' + gradient.id + ')'] = gradient.stops[0].color;
        }
        me.gradientsMap = gradientsMap;
    },

    
    hasCls: function(sprite, className) {
        return className && (' ' + (sprite.el.dom.getAttribute('class') || '') + ' ').indexOf(' ' + className + ' ') != -1;
    },

    addCls: function(sprite, className) {
        var el = sprite.el,
            i,
            len,
            v,
            cls = [],
            curCls =  el.getAttribute('class') || '';
        
        if (!Ext.isArray(className)) {
            if (typeof className == 'string' && !this.hasCls(sprite, className)) {
                el.set({ 'class': curCls + ' ' + className });
            }
        }
        else {
            for (i = 0, len = className.length; i < len; i++) {
                v = className[i];
                if (typeof v == 'string' && (' ' + curCls + ' ').indexOf(' ' + v + ' ') == -1) {
                    cls.push(v);
                }
            }
            if (cls.length) {
                el.set({ 'class': ' ' + cls.join(' ') });
            }
        }
    },

    removeCls: function(sprite, className) {
        var me = this,
            el = sprite.el,
            curCls =  el.getAttribute('class') || '',
            i, idx, len, cls, elClasses;
        if (!Ext.isArray(className)){
            className = [className];
        }
        if (curCls) {
            elClasses = curCls.replace(me.trimRe, ' ').split(me.spacesRe);
            for (i = 0, len = className.length; i < len; i++) {
                cls = className[i];
                if (typeof cls == 'string') {
                    cls = cls.replace(me.trimRe, '');
                    idx = Ext.Array.indexOf(elClasses, cls);
                    if (idx != -1) {
                        Ext.Array.erase(elClasses, idx, 1);
                    }
                }
            }
            el.set({ 'class': elClasses.join(' ') });
        }
    },

    destroy: function() {
        var me = this;
        
        me.callParent();
        if (me.el) {
            me.el.remove();
        }
        if (me._defs) {
            Ext.get(me._defs).destroy();
        }
        if (me.bgRect) {
            Ext.get(me.bgRect).destroy();
        }
        if (me.webkitRect) {
            Ext.get(me.webkitRect).destroy();
        }
        delete me.el;
    }
});


Ext.define('Ext.draw.engine.SvgExporter', function(){
   var commaRe = /,/g,
       fontRegex = /(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)\s('*.*'*)/,
       rgbColorRe = /rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g,
       rgbaColorRe = /rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,([\d\.]+)\)/g,
       surface, len, width, height,

   init = function(s){
       surface = s;
       len = surface.length;
       width = surface.width;
       height = surface.height;
   },
   spriteProcessor = {
       path: function(sprite){

           var attr = sprite.attr,
               path = attr.path,
               pathString = '',
               props, p, pLen;

           if (Ext.isArray(path[0])) {
               pLen = path.length;
               for (p = 0; p < pLen; p++) {
                   pathString += path[p].join(' ');
               }
           } else if (Ext.isArray(path)) {
               pathString = path.join(' ');
           } else {
               pathString = path.replace(commaRe,' ');
           }

           props = toPropertyString({
               d: pathString,
               fill: attr.fill || 'none',
               stroke: attr.stroke,
               'fill-opacity': attr.opacity,
               'stroke-width': attr['stroke-width'],
               'stroke-opacity': attr['stroke-opacity'],
               "z-index": attr.zIndex,
               transform: sprite.matrix.toSvg()
           });

           return '<path ' + props + '/>';
       },
       text: function(sprite){

           
           

           var attr = sprite.attr,
               match = fontRegex.exec(attr.font),
               size = (match && match[1]) || "12",
               
               family = (match && match[3]) || 'Arial',
               text = attr.text,
               factor = (Ext.isFF3_0 || Ext.isFF3_5) ? 2 : 4,
               tspanString = '',
               props;

           sprite.getBBox();
           tspanString += '<tspan x="' + (attr.x || '') + '" dy="';
           tspanString += (size/factor)+'">';
           tspanString += Ext.htmlEncode(text) + '</tspan>';


           props = toPropertyString({
               x: attr.x,
               y: attr.y,
               'font-size': size,
               'font-family': family,
               'font-weight': attr['font-weight'],
               'text-anchor': attr['text-anchor'],
               
               fill: attr.fill || '#000',
               'fill-opacity': attr.opacity,
               transform: sprite.matrix.toSvg()
           });



           return '<text '+ props + '>' +  tspanString + '</text>';
       },
       rect: function(sprite){

           var attr = sprite.attr,
               props =  toPropertyString({
                   x: attr.x,
                   y: attr.y,
                   rx: attr.rx,
                   ry: attr.ry,
                   width: attr.width,
                   height: attr.height,
                   fill: attr.fill || 'none',
                   'fill-opacity': attr.opacity,
                   stroke: attr.stroke,
                   'stroke-opacity': attr['stroke-opacity'],
                   'stroke-width':attr['stroke-width'],
                   transform: sprite.matrix && sprite.matrix.toSvg()
               });

           return '<rect ' + props + '/>';
       },
       circle: function(sprite){

           var attr = sprite.attr,
               props = toPropertyString({
                   cx: attr.x,
                   cy: attr.y,
                   r: attr.radius,
                   fill: attr.translation.fill || attr.fill || 'none',
                   'fill-opacity': attr.opacity,
                   stroke: attr.stroke,
                   'stroke-opacity': attr['stroke-opacity'],
                   'stroke-width':attr['stroke-width'],
                   transform: sprite.matrix.toSvg()
               });

           return '<circle ' + props + ' />';
       },
       image: function(sprite){

           var attr = sprite.attr,
               props = toPropertyString({
                   x: attr.x - (attr.width/2 >> 0),
                   y: attr.y - (attr.height/2 >> 0),
                   width: attr.width,
                   height: attr.height,
                   'xlink:href': attr.src,
                   transform: sprite.matrix.toSvg()
               });

           return '<image ' + props + ' />';
       }
   },
   svgHeader = function(){
       var svg = '<?xml version="1.0" standalone="yes"?>';
       svg += '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">';
       return svg;
   },
   svgContent = function(){
       var svg = '<svg width="'+width+'px" height="'+height+'px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">',
           defs = '', item, itemsLen, items, gradient,
           getSvgString, colorstops, stop,
           coll, keys, colls, k, kLen, key, collI, i, j, stopsLen, sortedItems, za, zb;

       items = surface.items.items;
       itemsLen = items.length;


       getSvgString = function(node){

           var childs = node.childNodes,
               childLength = childs.length,
               i = 0,
               attrLength,
               j,
               svgString = '', child, attr, tagName, attrItem;

               for(; i < childLength; i++){
                   child = childs[i];
                   attr = child.attributes;
                   tagName = child.tagName;

                   svgString += '<' +tagName;

                   for(j = 0, attrLength = attr.length; j < attrLength; j++){
                       attrItem = attr.item(j);
                       svgString += ' '+attrItem.name+'="'+attrItem.value+'"';
                   }

                   svgString += '>';

                   if(child.childNodes.length > 0){
                       svgString += getSvgString(child);
                   }

                   svgString += '</' + tagName + '>';

               }
           return svgString;
       };


       if(surface.getDefs){
           defs = getSvgString(surface.getDefs());
       }else{
           
           coll = surface.gradientsColl;
           if (coll) {
               keys  = coll.keys;
               colls = coll.items;
               k     = 0;
               kLen  = keys.length;
           }

           for (; k < kLen; k++) {
               key   = keys[k];
               collI = colls[k];

               gradient = surface.gradientsColl.getByKey(key);
               defs += '<linearGradient id="' + key + '" x1="0" y1="0" x2="1" y2="1">';

               var color = gradient.colors.replace(rgbColorRe, 'rgb($1|$2|$3)');
               color = color.replace(rgbaColorRe, 'rgba($1|$2|$3|$4)')
               colorstops = color.split(',');
               for(i=0, stopsLen = colorstops.length; i < stopsLen; i++){
                   stop = colorstops[i].split(' ');
                   color = Ext.draw.Color.fromString(stop[1].replace(/\|/g,','));
                   defs += '<stop offset="'+stop[0]+'" stop-color="' + color.toString() + '" stop-opacity="1"></stop>';
               }
               defs += '</linearGradient>';
           }
       }

       svg += '<defs>' + defs + '</defs>';

       
       svg += spriteProcessor.rect({
           attr: {
                   width: '100%',
                   height: '100%',
                   fill: '#fff',
                   stroke: 'none',
                   opacity: '0'
           }
       });

       
       sortedItems = new Array(itemsLen);
       for(i = 0; i < itemsLen; i++){
           sortedItems[i] = i;
       }
       sortedItems.sort(function (a, b) {
           za = items[a].attr.zIndex || 0;
           zb = items[b].attr.zIndex || 0;
           if (za == zb) {
               return a - b;
           }
           return za - zb;
       });

       for(i = 0; i < itemsLen; i++){
           item = items[sortedItems[i]];
           if(!item.attr.hidden){
               svg += spriteProcessor[item.type](item);
           }
       }

       svg += '</svg>';

       return svg;
   },
   toPropertyString = function(obj){
       var propString = '',
           key;

       for(key in obj){

           if(obj.hasOwnProperty(key) && obj[key] != null){
               propString += key +'="'+ obj[key]+'" ';
           }

       }

       return propString;
   };

   return {
       singleton: true,

       
       generate: function(surface, config){
           config = config || {};
           init(surface);
           return svgHeader() + svgContent();
       }
   };
});


Ext.define('Ext.draw.engine.Vml', {

    

    extend:  Ext.draw.Surface ,

                                                                                                       

    

    engine: 'Vml',

    map: {M: "m", L: "l", C: "c", Z: "x", m: "t", l: "r", c: "v", z: "x"},
    bitesRe: /([clmz]),?([^clmz]*)/gi,
    valRe: /-?[^,\s\-]+/g,
    fillUrlRe: /^url\(\s*['"]?([^\)]+?)['"]?\s*\)$/i,
    pathlike: /^(path|rect)$/,
    NonVmlPathRe: /[ahqstv]/ig, // Non-VML Pathing ops
    partialPathRe: /[clmz]/g,
    fontFamilyRe: /^['"]+|['"]+$/g,
    baseVmlCls: Ext.baseCSSPrefix + 'vml-base',
    vmlGroupCls: Ext.baseCSSPrefix + 'vml-group',
    spriteCls: Ext.baseCSSPrefix + 'vml-sprite',
    measureSpanCls: Ext.baseCSSPrefix + 'vml-measure-span',
    zoom: 21600,
    coordsize: 1000,
    coordorigin: '0 0',
    zIndexShift: 0,
    // VML uses CSS z-index and therefore doesn't need sprites to be kept in zIndex order
    orderSpritesByZIndex: false,

    
    
    path2vml: function (path) {
        var me = this,
            nonVML = me.NonVmlPathRe,
            map = me.map,
            val = me.valRe,
            zoom = me.zoom,
            bites = me.bitesRe,
            command = Ext.Function.bind(Ext.draw.Draw.pathToAbsolute, Ext.draw.Draw),
            res, pa, p, r, i, ii, j, jj;
        if (String(path).match(nonVML)) {
            command = Ext.Function.bind(Ext.draw.Draw.path2curve, Ext.draw.Draw);
        } else if (!String(path).match(me.partialPathRe)) {
            res = String(path).replace(bites, function (all, command, args) {
                var vals = [],
                    isMove = command.toLowerCase() == "m",
                    res = map[command];
                args.replace(val, function (value) {
                    if (isMove && vals.length === 2) {
                        res += vals + map[command == "m" ? "l" : "L"];
                        vals = [];
                    }
                    vals.push(Math.round(value * zoom));
                });
                return res + vals;
            });
            return res;
        }
        pa = command(path);
        res = [];
        for (i = 0, ii = pa.length; i < ii; i++) {
            p = pa[i];
            r = pa[i][0].toLowerCase();
            if (r == "z") {
                r = "x";
            }
            for (j = 1, jj = p.length; j < jj; j++) {
                r += Math.round(p[j] * me.zoom) + (j != jj - 1 ? "," : "");
            }
            res.push(r);
        }
        return res.join(" ");
    },

    
    translateAttrs: {
        radius: "r",
        radiusX: "rx",
        radiusY: "ry",
        lineWidth: "stroke-width",
        fillOpacity: "fill-opacity",
        strokeOpacity: "stroke-opacity",
        strokeLinejoin: "stroke-linejoin"
    },

    
    minDefaults: {
        circle: {
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        ellipse: {
            cx: 0,
            cy: 0,
            rx: 0,
            ry: 0,
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        rect: {
            x: 0,
            y: 0,
            width: 0,
            height: 0,
            rx: 0,
            ry: 0,
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        text: {
            x: 0,
            y: 0,
            "text-anchor": "start",
            font: '10px "Arial"',
            fill: "#000",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        path: {
            d: "M0,0",
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        image: {
            x: 0,
            y: 0,
            width: 0,
            height: 0,
            preserveAspectRatio: "none",
            opacity: null
        }
    },

    
    onMouseEnter: function (e) {
        this.fireEvent("mouseenter", e);
    },

    
    onMouseLeave: function (e) {
        this.fireEvent("mouseleave", e);
    },

    
    processEvent: function (name, e) {
        var target = e.getTarget(),
            surface = this.surface,
            sprite;
        this.fireEvent(name, e);
        sprite = this.items.get(target.id);
        if (sprite) {
            sprite.fireEvent(name, sprite, e);
        }
    },

    
    createSpriteElement: function (sprite) {
        var me = this,
            attr = sprite.attr,
            type = sprite.type,
            zoom = me.zoom,
            vml = sprite.vml || (sprite.vml = {}),
            round = Math.round,
            el = (type === 'image') ? me.createNode('image') : me.createNode('shape'),
            path, skew, textPath;

        el.coordsize = zoom + ' ' + zoom;
        el.coordorigin = attr.coordorigin || "0 0";
        Ext.get(el).addCls(me.spriteCls);
        if (type == "text") {
            vml.path = path = me.createNode("path");
            path.textpathok = true;
            vml.textpath = textPath = me.createNode("textpath");
            textPath.on = true;
            el.appendChild(textPath);
            el.appendChild(path);
        }
        el.id = sprite.id;
        sprite.el = Ext.get(el);
        sprite.el.setStyle('zIndex', -me.zIndexShift);
        me.el.appendChild(el);
        if (type !== 'image') {
            skew = me.createNode("skew");
            skew.on = true;
            el.appendChild(skew);
            sprite.skew = skew;
        }
        sprite.matrix = new Ext.draw.Matrix();
        sprite.bbox = {
            plain: null,
            transform: null
        };

        this.applyAttrs(sprite);
        this.applyTransformations(sprite);        
        sprite.fireEvent("render", sprite);
        return sprite.el;
    },

    getBBoxText: function (sprite) {
        var vml = sprite.vml;
        return {
            x: vml.X + (vml.bbx || 0) - vml.W / 2,
            y: vml.Y - vml.H / 2,
            width: vml.W,
            height: vml.H
        };
    },

    applyAttrs: function (sprite) {
        var me = this,
            vml = sprite.vml,
            group = sprite.group,
            spriteAttr = sprite.attr,
            el = sprite.el,
            dom = el.dom,
            style, name, groups, i, ln, scrubbedAttrs, font, key,
            cx, cy, rx, ry;

        if (group) {
            groups = [].concat(group);
            ln = groups.length;
            for (i = 0; i < ln; i++) {
                group = groups[i];
                me.getGroup(group).add(sprite);
            }
            delete sprite.group;
        }
        scrubbedAttrs = me.scrubAttrs(sprite) || {};

        if (sprite.zIndexDirty) {
            me.setZIndex(sprite);
        }

        
        Ext.applyIf(scrubbedAttrs, me.minDefaults[sprite.type]);

        if (sprite.type == 'image') {
            Ext.apply(sprite.attr, {
                x: scrubbedAttrs.x,
                y: scrubbedAttrs.y,
                width: scrubbedAttrs.width,
                height: scrubbedAttrs.height
            });
            el.setStyle({
                width: scrubbedAttrs.width + 'px',
                height: scrubbedAttrs.height + 'px'
            });
            dom.src = scrubbedAttrs.src;
        }

        if (dom.href) {
            dom.href = scrubbedAttrs.href;
        }
        if (dom.title) {
            dom.title = scrubbedAttrs.title;
        }
        if (dom.target) {
            dom.target = scrubbedAttrs.target;
        }
        if (dom.cursor) {
            dom.cursor = scrubbedAttrs.cursor;
        }

        
        if (sprite.dirtyHidden) {
            (scrubbedAttrs.hidden) ? me.hidePrim(sprite) : me.showPrim(sprite);
            sprite.dirtyHidden = false;
        }

        
        if (sprite.dirtyPath) {
            if (sprite.type == "circle" || sprite.type == "ellipse") {
                cx = scrubbedAttrs.x;
                cy = scrubbedAttrs.y;
                rx = scrubbedAttrs.rx || scrubbedAttrs.r || 0;
                ry = scrubbedAttrs.ry || scrubbedAttrs.r || 0;
                dom.path = Ext.String.format("ar{0},{1},{2},{3},{4},{1},{4},{1}",
                    Math.round((cx - rx) * me.zoom),
                    Math.round((cy - ry) * me.zoom),
                    Math.round((cx + rx) * me.zoom),
                    Math.round((cy + ry) * me.zoom),
                    Math.round(cx * me.zoom));
                sprite.dirtyPath = false;
            }
            else if (sprite.type !== "text" && sprite.type !== 'image') {
                sprite.attr.path = scrubbedAttrs.path = me.setPaths(sprite, scrubbedAttrs) || scrubbedAttrs.path;
                dom.path = me.path2vml(scrubbedAttrs.path);
                sprite.dirtyPath = false;
            }
        }

        
        if ("clip-rect" in scrubbedAttrs) {
            me.setClip(sprite, scrubbedAttrs);
        }

        
        if (sprite.type == "text") {
            me.setTextAttributes(sprite, scrubbedAttrs);
        }

        
        if (scrubbedAttrs.opacity || scrubbedAttrs['stroke-opacity'] || scrubbedAttrs.fill) {
            me.setFill(sprite, scrubbedAttrs);
        }

        
        if (scrubbedAttrs.stroke || scrubbedAttrs['stroke-opacity'] || scrubbedAttrs.fill) {
            me.setStroke(sprite, scrubbedAttrs);
        }

        
        style = spriteAttr.style;
        if (style) {
            el.setStyle(style);
        }

        sprite.dirty = false;
    },

    setZIndex: function (sprite) {
        var me = this,
            zIndex = sprite.attr.zIndex,
            shift = me.zIndexShift,
            items, iLen, item, i;

        if (zIndex < shift) {
            
            
            items = me.items.items;
            iLen = items.length;

            for (i = 0; i < iLen; i++) {
                if ((zIndex = items[i].attr.zIndex) && zIndex < shift) { 
                    shift = zIndex;
                }
            }

            me.zIndexShift = shift;
            for (i = 0; i < iLen; i++) {
                item = items[i];
                if (item.el) {
                    item.el.setStyle('zIndex', item.attr.zIndex - shift);
                }
                item.zIndexDirty = false;
            }
        } else if (sprite.el) {
            sprite.el.setStyle('zIndex', zIndex - shift);
            sprite.zIndexDirty = false;
        }
    },

    
    setPaths: function (sprite, params) {
        var spriteAttr = sprite.attr, thickness = sprite.attr['stroke-width'] || 1;
        
        sprite.bbox.plain = null;
        sprite.bbox.transform = null;
        if (sprite.type == 'circle') {
            spriteAttr.rx = spriteAttr.ry = params.r;
            return Ext.draw.Draw.ellipsePath(sprite);
        }
        else if (sprite.type == 'ellipse') {
            spriteAttr.rx = params.rx;
            spriteAttr.ry = params.ry;
            return Ext.draw.Draw.ellipsePath(sprite);
        }
        else if (sprite.type == 'rect') {
            spriteAttr.rx = spriteAttr.ry = params.r;
            return Ext.draw.Draw.rectPath(sprite);
        }
        else if (sprite.type == 'path' && spriteAttr.path) {
            return Ext.draw.Draw.pathToAbsolute(spriteAttr.path);
        }
        return false;
    },

    setFill: function (sprite, params) {
        var me = this,
            el = sprite.el.dom,
            fillEl = el.fill,
            newfill = false,
            opacity, gradient, fillUrl, rotation, angle;

        if (!fillEl) {
            
            fillEl = el.fill = me.createNode("fill");
            newfill = true;
        }
        if (Ext.isArray(params.fill)) {
            params.fill = params.fill[0];
        }
        if (params.fill == "none") {
            fillEl.on = false;
        }
        else {
            if (typeof params.opacity == "number") {
                fillEl.opacity = params.opacity;
            }
            if (typeof params["fill-opacity"] == "number") {
                fillEl.opacity = params["fill-opacity"];
            }
            fillEl.on = true;
            if (typeof params.fill == "string") {
                fillUrl = params.fill.match(me.fillUrlRe);
                if (fillUrl) {
                    fillUrl = fillUrl[1];
                    
                    if (fillUrl.charAt(0) == "#") {
                        gradient = me.gradientsColl.getByKey(fillUrl.substring(1));
                    }
                    if (gradient) {
                        
                        rotation = params.rotation;
                        angle = -(gradient.angle + 270 + (rotation ? rotation.degrees : 0)) % 360;
                        
                        if (angle === 0) {
                            angle = 180;
                        }
                        fillEl.angle = angle;
                        fillEl.type = "gradient";
                        fillEl.method = "sigma";
                        if (fillEl.colors) {
                            fillEl.colors.value = gradient.colors;
                        } else {
                            fillEl.colors = gradient.colors;
                        }
                    }
                    
                    else {
                        fillEl.src = fillUrl;
                        fillEl.type = "tile";
                    }
                }
                else {
                    fillEl.color = Ext.draw.Color.toHex(params.fill);
                    fillEl.src = "";
                    fillEl.type = "solid";
                }
            }
        }
        if (newfill) {
            el.appendChild(fillEl);
        }
    },

    setStroke: function (sprite, params) {
        var me = this,
            el = sprite.el.dom,
            strokeEl = sprite.strokeEl,
            newStroke = false,
            width, opacity;

        if (!strokeEl) {
            strokeEl = sprite.strokeEl = me.createNode("stroke");
            newStroke = true;
        }
        if (Ext.isArray(params.stroke)) {
            params.stroke = params.stroke[0];
        }
        if (!params.stroke || params.stroke == "none" || params.stroke == 0 || params["stroke-width"] == 0) {
            strokeEl.on = false;
        }
        else {
            strokeEl.on = true;
            if (params.stroke && !params.stroke.match(me.fillUrlRe)) {
                
                strokeEl.color = Ext.draw.Color.toHex(params.stroke);
            }
            strokeEl.dashstyle = params["stroke-dasharray"] ? "dash" : "solid";
            strokeEl.joinstyle = params["stroke-linejoin"];
            strokeEl.endcap = params["stroke-linecap"] || "round";
            strokeEl.miterlimit = params["stroke-miterlimit"] || 8;
            width = parseFloat(params["stroke-width"] || 1) * 0.75;
            opacity = params["stroke-opacity"] || 1;
            
            if (Ext.isNumber(width) && width < 1) {
                strokeEl.weight = 1;
                strokeEl.opacity = opacity * width;
            }
            else {
                strokeEl.weight = width;
                strokeEl.opacity = opacity;
            }
        }
        if (newStroke) {
            el.appendChild(strokeEl);
        }
    },

    setClip: function (sprite, params) {
        var me = this,
            el = sprite.el,
            clipEl = sprite.clipEl,
            rect = String(params["clip-rect"]).split(me.separatorRe);
        if (!clipEl) {
            clipEl = sprite.clipEl = me.el.insertFirst(Ext.getDoc().dom.createElement("div"));
            clipEl.addCls(Ext.baseCSSPrefix + 'vml-sprite');
        }
        if (rect.length == 4) {
            rect[2] = +rect[2] + (+rect[0]);
            rect[3] = +rect[3] + (+rect[1]);
            clipEl.setStyle("clip", Ext.String.format("rect({1}px {2}px {3}px {0}px)", rect[0], rect[1], rect[2], rect[3]));
            clipEl.setSize(me.el.width, me.el.height);
        }
        else {
            clipEl.setStyle("clip", "");
        }
    },

    setTextAttributes: function (sprite, params) {
        var me = this,
            vml = sprite.vml,
            textStyle = vml.textpath.style,
            spanCacheStyle = me.span.style,
            zoom = me.zoom,
            round = Math.round,
            fontObj = {
                fontSize: "font-size",
                fontWeight: "font-weight",
                fontStyle: "font-style"
            },
            fontProp,
            paramProp;
        if (sprite.dirtyFont) {
            if (params.font) {
                textStyle.font = spanCacheStyle.font = params.font;
            }
            if (params["font-family"]) {
                textStyle.fontFamily = '"' + params["font-family"].split(",")[0].replace(me.fontFamilyRe, "") + '"';
                spanCacheStyle.fontFamily = params["font-family"];
            }

            for (fontProp in fontObj) {
                paramProp = params[fontObj[fontProp]];
                if (paramProp) {
                    textStyle[fontProp] = spanCacheStyle[fontProp] = paramProp;
                }
            }

            me.setText(sprite, params.text);

            if (vml.textpath.string) {
                me.span.innerHTML = String(vml.textpath.string).replace(/</g, "&#60;").replace(/&/g, "&#38;").replace(/\n/g, "<br/>");
            }
            vml.W = me.span.offsetWidth;
            vml.H = me.span.offsetHeight + 2; 

            
            if (params["text-anchor"] == "middle") {
                textStyle["v-text-align"] = "center";
            }
            else if (params["text-anchor"] == "end") {
                textStyle["v-text-align"] = "right";
                vml.bbx = -Math.round(vml.W / 2);
            }
            else {
                textStyle["v-text-align"] = "left";
                vml.bbx = Math.round(vml.W / 2);
            }
        }
        vml.X = params.x;
        vml.Y = params.y;
        vml.path.v = Ext.String.format("m{0},{1}l{2},{1}", Math.round(vml.X * zoom), Math.round(vml.Y * zoom), Math.round(vml.X * zoom) + 1);
        
        sprite.bbox.plain = null;
        sprite.bbox.transform = null;
        sprite.dirtyFont = false;
    },

    setText: function (sprite, text) {
        sprite.vml.textpath.string = Ext.htmlDecode(text);
    },

    hide: function () {
        this.el.hide();
    },

    show: function () {
        this.el.show();
    },

    hidePrim: function (sprite) {
        sprite.el.addCls(Ext.baseCSSPrefix + 'hide-visibility');
    },

    showPrim: function (sprite) {
        sprite.el.removeCls(Ext.baseCSSPrefix + 'hide-visibility');
    },

    setSize: function (width, height) {
        var me = this;
        width = width || me.width;
        height = height || me.height;
        me.width = width;
        me.height = height;

        if (me.el) {
            
            if (width != undefined) {
                me.el.setWidth(width);
            }
            if (height != undefined) {
                me.el.setHeight(height);
            }
        }

        me.callParent(arguments);
    },

    
    applyViewBox: function () {
        var me = this,
            viewBox = me.viewBox,
            width = me.width,
            height = me.height,
            items,
            iLen,
            i;
        
        me.callParent();

        if (viewBox && (width || height)) {
            items = me.items.items;
            iLen = items.length;

            for (i = 0; i < iLen; i++) {
                me.applyTransformations(items[i]);
            }
        }
    },

    onAdd: function (item) {
        this.callParent(arguments);
        if (this.el) {
            this.renderItem(item);
        }
    },

    onRemove: function (sprite) {
        if (sprite.el) {
            sprite.el.remove();
            delete sprite.el;
        }
        this.callParent(arguments);
    },

    render: function (container) {
        var me = this,
            doc = Ext.getDoc().dom,
            el;
        
        if (!me.createNode) {
            try {
                if (!doc.namespaces.rvml) {
                    doc.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
                }
                me.createNode = function (tagName) {
                    return doc.createElement("<rvml:" + tagName + ' class="rvml">');
                };
            } catch (e) {
                me.createNode = function (tagName) {
                    return doc.createElement("<" + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
                };
            }
        }

        if (!me.el) {
            el = doc.createElement("div");
            me.el = Ext.get(el);
            me.el.addCls(me.baseVmlCls);

            
            me.span = doc.createElement("span");
            Ext.get(me.span).addCls(me.measureSpanCls);
            el.appendChild(me.span);
            me.el.setSize(me.width || 0, me.height || 0);
            container.appendChild(el);
            me.el.on({
                scope: me,
                mouseup: me.onMouseUp,
                mousedown: me.onMouseDown,
                mouseover: me.onMouseOver,
                mouseout: me.onMouseOut,
                mousemove: me.onMouseMove,
                mouseenter: me.onMouseEnter,
                mouseleave: me.onMouseLeave,
                click: me.onClick,
                dblclick: me.onDblClick
            });
        }
        me.renderAll();
    },

    renderAll: function () {
        this.items.each(this.renderItem, this);
    },

    redraw: function (sprite) {
        sprite.dirty = true;
        this.renderItem(sprite);
    },

    renderItem: function (sprite) {
        
        if (!this.el) {
            return;
        }

        
        if (!sprite.el) {
            this.createSpriteElement(sprite);
        }

        if (sprite.dirty) {
            this.applyAttrs(sprite);
            if (sprite.dirtyTransform) {
                this.applyTransformations(sprite);
            }
        }
    },

    rotationCompensation: function (deg, dx, dy) {
        var matrix = new Ext.draw.Matrix();
        matrix.rotate(-deg, 0.5, 0.5);
        return {
            x: matrix.x(dx, dy),
            y: matrix.y(dx, dy)
        };
    },

    transform: function (sprite, matrixOnly) {
        var me = this,
            bbox = me.getBBox(sprite, true),
            cx = bbox.x + bbox.width * 0.5,
            cy = bbox.y + bbox.height * 0.5,
            matrix = new Ext.draw.Matrix(),
            transforms = sprite.transformations,
            transformsLength = transforms.length,
            i = 0,
            deltaDegrees = 0,
            deltaScaleX = 1,
            deltaScaleY = 1,
            flip = "",
            el = sprite.el,
            dom = el.dom,
            domStyle = dom.style,
            zoom = me.zoom,
            skew = sprite.skew,
            shift = me.viewBoxShift,
            deltaX, deltaY, transform, type, compensate, y, fill, newAngle, zoomScaleX, zoomScaleY, newOrigin, offset;


        for (; i < transformsLength; i++) {
            transform = transforms[i];
            type = transform.type;
            if (type == "translate") {
                matrix.translate(transform.x, transform.y);
            }
            else if (type == "rotate") {
                matrix.rotate(transform.degrees, transform.x, transform.y);
                deltaDegrees += transform.degrees;
            }
            else if (type == "scale") {
                matrix.scale(transform.x, transform.y, transform.centerX, transform.centerY);
                deltaScaleX *= transform.x;
                deltaScaleY *= transform.y;
            }
        }

        sprite.matrix = matrix.clone();

        if (matrixOnly) {
            return;
        }

        if (shift) {
            matrix.prepend(shift.scale, 0, 0, shift.scale, shift.dx * shift.scale, shift.dy * shift.scale);
        }

        
        if (sprite.type != "image" && skew) {
            skew.origin = "0,0";
            
            skew.matrix = matrix.toString();
            
            
            
            offset = matrix.offset();
            if (offset[0] > 32767) {
                offset[0] = 32767;
            } else if (offset[0] < -32768) {
                offset[0] = -32768;
            }
            if (offset[1] > 32767) {
                offset[1] = 32767;
            } else if (offset[1] < -32768) {
                offset[1] = -32768;
            }
            skew.offset = offset;
        }
        else {
            domStyle.filter = matrix.toFilter();
            domStyle.left = Math.min(
                matrix.x(bbox.x, bbox.y),
                matrix.x(bbox.x + bbox.width, bbox.y),
                matrix.x(bbox.x, bbox.y + bbox.height),
                matrix.x(bbox.x + bbox.width, bbox.y + bbox.height)) + 'px';
            domStyle.top = Math.min(
                matrix.y(bbox.x, bbox.y),
                matrix.y(bbox.x + bbox.width, bbox.y),
                matrix.y(bbox.x, bbox.y + bbox.height),
                matrix.y(bbox.x + bbox.width, bbox.y + bbox.height)) + 'px';
        }
    },

    createItem: function (config) {
        return Ext.create('Ext.draw.Sprite', config);
    },

    getRegion: function () {
        return this.el.getRegion();
    },

    addCls: function (sprite, className) {
        if (sprite && sprite.el) {
            sprite.el.addCls(className);
        }
    },

    removeCls: function (sprite, className) {
        if (sprite && sprite.el) {
            sprite.el.removeCls(className);
        }
    },

    
    addGradient: function (gradient) {
        var gradients = this.gradientsColl || (this.gradientsColl = Ext.create('Ext.util.MixedCollection')),
            colors = [],
            stops = Ext.create('Ext.util.MixedCollection'),
            keys,
            items,
            iLen,
            key,
            item,
            i;

        
        stops.addAll(gradient.stops);
        stops.sortByKey("ASC", function (a, b) {
            a = parseInt(a, 10);
            b = parseInt(b, 10);
            return a > b ? 1 : (a < b ? -1 : 0);
        });

        keys = stops.keys;
        items = stops.items;
        iLen = keys.length;

        for (i = 0; i < iLen; i++) {
            key = keys[i];
            item = items[i];
            colors.push(key + '% ' + item.color);
        }

        gradients.add(gradient.id, {
            colors: colors.join(","),
            angle: gradient.angle
        });
    },

    destroy: function () {
        var me = this;

        me.callParent(arguments);
        if (me.el) {
            me.el.remove();
        }
        delete me.el;
    }
});















Ext.define('Ext.flash.Component', {
    extend:  Ext.Component ,
    alternateClassName: 'Ext.FlashComponent',
    alias: 'widget.flash',

    
    flashVersion : '9.0.115',

    
    backgroundColor: '#ffffff',

    
    wmode: 'opaque',

    

    

    

    

    
    swfWidth: '100%',

    
    swfHeight: '100%',

    
    expressInstall: false,

    

    
    renderTpl: ['<div id="{swfId}" role="presentation"></div>'],

    initComponent: function() {

        this.callParent();
        this.addEvents(
            
            'success',

            
            'failure'
        );
    },
    
    beforeRender: function(){
        this.callParent();
        
        Ext.applyIf(this.renderData, {
            swfId: this.getSwfId()
        });
    },

    afterRender: function() {
        var me = this,
            flashParams = Ext.apply({}, me.flashParams), 
            flashVars = Ext.apply({}, me.flashVars);

        me.callParent();

        flashParams = Ext.apply({
            allowScriptAccess: 'always',
            bgcolor: me.backgroundColor,
            wmode: me.wmode
        }, flashParams);

        flashVars = Ext.apply({
            allowedDomain: document.location.hostname
        }, flashVars);

        new swfobject.embedSWF(
            me.url,
            me.getSwfId(),
            me.swfWidth,
            me.swfHeight,
            me.flashVersion,
            me.expressInstall ? me.statics.EXPRESS_INSTALL_URL : undefined,
            flashVars,
            flashParams,
            me.flashAttributes,
            Ext.bind(me.swfCallback, me)
        );
    },

    
    swfCallback: function(e) {
        var me = this;
        if (e.success) {
            me.swf = Ext.get(e.ref);
            me.onSuccess();
            me.fireEvent('success', me);
        } else {
            me.onFailure();
            me.fireEvent('failure', me);
        }
    },

    
    getSwfId: function() {
        return this.swfId || (this.swfId = "extswf" + this.getAutoId());
    },

    onSuccess: function() {
        
        
        this.swf.setStyle('visibility', 'inherit');
    },

    onFailure: Ext.emptyFn,

    beforeDestroy: function() {
        var me = this,
            swf = me.swf;
        if (swf) {
            swfobject.removeSWF(me.getSwfId());
            Ext.destroy(swf);
            delete me.swf;
        }
        me.callParent();
    },

    statics: {
        
        EXPRESS_INSTALL_URL: 'http:/' + '/swfobject.googlecode.com/svn/trunk/swfobject/expressInstall.swf'
    }
});


Ext.define('Ext.form.action.Action', {
    alternateClassName: 'Ext.form.Action',

    

    

    

    

    

    

    

    

    

    

    

    

    
    submitEmptyText : true,

    

    

    

    

    
    constructor: function(config) {
        if (config) {
            Ext.apply(this, config);
        }

        
        var params = config.params;
        if (Ext.isString(params)) {
            this.params = Ext.Object.fromQueryString(params);
        }
    },

    
    run: Ext.emptyFn,

    

    

    
    onFailure : function(response){
        this.response = response;
        this.failureType = Ext.form.action.Action.CONNECT_FAILURE;
        this.form.afterAction(this, false);
    },

    
    processResponse : function(response){
        this.response = response;
        if (!response.responseText && !response.responseXML) {
            return true;
        }
        return (this.result = this.handleResponse(response));
    },

    
    getUrl: function() {
        return this.url || this.form.url;
    },

    
    getMethod: function() {
        return (this.method || this.form.method || 'POST').toUpperCase();
    },

    
    getParams: function() {
        return Ext.apply({}, this.params, this.form.baseParams);
    },

    
    createCallback: function() {
        var me = this,
            undef,
            form = me.form;
        return {
            success: me.onSuccess,
            failure: me.onFailure,
            scope: me,
            timeout: (this.timeout * 1000) || (form.timeout * 1000),
            upload: form.fileUpload ? me.onSuccess : undef
        };
    },

    statics: {
        
        CLIENT_INVALID: 'client',

        
        SERVER_INVALID: 'server',

        
        CONNECT_FAILURE: 'connect',

        
        LOAD_FAILURE: 'load'


    }
});


Ext.define('Ext.form.action.Load', {
    extend: Ext.form.action.Action ,
                                      
    alternateClassName: 'Ext.form.Action.Load',
    alias: 'formaction.load',

    type: 'load',

    
    run: function() {
        Ext.Ajax.request(Ext.apply(
            this.createCallback(),
            {
                method: this.getMethod(),
                url: this.getUrl(),
                headers: this.headers,
                params: this.getParams()
            }
        ));
    },

    
    onSuccess: function(response){
        var result = this.processResponse(response),
            form = this.form;
        if (result === true || !result.success || !result.data) {
            this.failureType = Ext.form.action.Action.LOAD_FAILURE;
            form.afterAction(this, false);
            return;
        }
        form.clearInvalid();
        form.setValues(result.data);
        form.afterAction(this, true);
    },

    
    handleResponse: function(response) {
        var reader = this.form.reader,
            rs, data;
        if (reader) {
            rs = reader.read(response);
            data = rs.records && rs.records[0] ? rs.records[0].data : null;
            return {
                success : rs.success,
                data : data
            };
        }
        return Ext.decode(response.responseText);
    }
});



Ext.define('Ext.form.action.Submit', {
    extend: Ext.form.action.Action ,
    alternateClassName: 'Ext.form.Action.Submit',
    alias: 'formaction.submit',

    type: 'submit',

    

    
    run : function(){
        var me = this,
            form = me.form;
            
        if (me.clientValidation === false || form.isValid()) {
            me.doSubmit();
        } else {
            
            me.failureType = Ext.form.action.Action.CLIENT_INVALID;
            form.afterAction(me, false);
        }
    },

    
    doSubmit: function() {
        var me = this,
            ajaxOptions = Ext.apply(me.createCallback(), {
                url: me.getUrl(),
                method: me.getMethod(),
                headers: me.headers
            }),
            form = me.form,
            jsonSubmit = me.jsonSubmit || form.jsonSubmit,
            paramsProp = jsonSubmit ? 'jsonData' : 'params',
            formInfo;

        
        
        if (form.hasUpload()) {
            formInfo = me.buildForm();
            ajaxOptions.form = formInfo.formEl;
            ajaxOptions.isUpload = true;
        } else {
            ajaxOptions[paramsProp] = me.getParams(jsonSubmit);
        }

        Ext.Ajax.request(ajaxOptions);
        if (formInfo) {
            me.cleanup(formInfo);
        }
    },
    
    cleanup: function(formInfo) {
        var formEl = formInfo.formEl,
            uploadEls = formInfo.uploadEls,
            uploadFields = formInfo.uploadFields,
            len = uploadFields.length,
            i, field;
            
        for (i = 0; i < len; ++i) {
            field = uploadFields[i];
            if (!field.clearOnSubmit) {
                field.restoreInput(uploadEls[i]);
            }    
        }
        
        if (formEl) {
            Ext.removeNode(formEl);
        }    
    },

    
    getParams: function(useModelValues) {
        var falseVal = false,
            configParams = this.callParent(),
            fieldParams = this.form.getValues(falseVal, falseVal, this.submitEmptyText !== falseVal, useModelValues,  true);
        return Ext.apply({}, fieldParams, configParams);
    },

    
    buildForm: function() {
        var me = this,
            fieldsSpec = [],
            formSpec,
            formEl,
            basicForm = me.form,
            params = me.getParams(),
            uploadFields = [],
            uploadEls = [],
            fields = basicForm.getFields().items,
            i,
            len   = fields.length,
            field, key, value, v, vLen,
            el;

        for (i = 0; i < len; ++i) {
            field = fields[i];

            if (field.isFileUpload()) {
                uploadFields.push(field);
            }
        }

        for (key in params) {
            if (params.hasOwnProperty(key)) {
                value = params[key];

                if (Ext.isArray(value)) {
                    vLen = value.length;
                    for (v = 0; v < vLen; v++) {
                        fieldsSpec.push(me.getFieldConfig(key, value[v]));
                    }
                } else {
                    fieldsSpec.push(me.getFieldConfig(key, value));
                }
            }
        }

        formSpec = {
            tag: 'form',
            role: 'presentation',
            action: me.getUrl(),
            method: me.getMethod(),
            target: me.target || '_self',
            style: 'display:none',
            cn: fieldsSpec
        };

        
        if (uploadFields.length) {
            formSpec.encoding = formSpec.enctype = 'multipart/form-data';
        }

        
        formEl = Ext.DomHelper.append(Ext.getBody(), formSpec);

        
        
        
        len = uploadFields.length;

        for (i = 0; i < len; ++i) {
            el = uploadFields[i].extractFileInput();
            formEl.appendChild(el);
            uploadEls.push(el);
        }

        return {
            formEl: formEl,
            uploadFields: uploadFields,
            uploadEls: uploadEls
        };
    },

    getFieldConfig: function(name, value) {
        return {
            tag: 'input',
            type: 'hidden',
            name: name,
            value: Ext.String.htmlEncode(value)
        };
    },

    
    onSuccess: function(response) {
        var form = this.form,
            success = true,
            result = this.processResponse(response);
        if (result !== true && !result.success) {
            if (result.errors) {
                form.markInvalid(result.errors);
            }
            this.failureType = Ext.form.action.Action.SERVER_INVALID;
            success = false;
        }
        form.afterAction(this, success);
    },

    
    handleResponse: function(response) {
        var form = this.form,
            errorReader = form.errorReader,
            rs, errors, i, len, records, result;
            
        if (errorReader) {
            rs = errorReader.read(response);
            records = rs.records;
            errors = [];
            if (records) {
                for(i = 0, len = records.length; i < len; i++) {
                    errors[i] = records[i].data;
                }
            }
            if (errors.length < 1) {
                errors = null;
            }
            result = {
                success : rs.success,
                errors : errors
            };
        } else {
            try {
                result = Ext.decode(response.responseText);    
            } catch (e) {
                result = {
                    success: false,
                    errors: []
                };
            }
            
        }
        return result;
    }
});


Ext.define('Ext.util.ComponentDragger', {
    extend:  Ext.dd.DragTracker ,

    

    

    

    autoStart: 500,

    
    constructor: function(comp, config) {
        this.comp = comp;
        this.initialConstrainTo = config.constrainTo;
        this.callParent([ config ]);
    },

    onStart: function(e) {
        var me = this,
            comp = me.comp;

        
        me.startPosition = comp.getXY();

        
        
        if (comp.ghost && !comp.liveDrag) {
             me.proxy = comp.ghost();
             me.dragTarget = me.proxy.header.el;
        }

        
        if (me.constrain || me.constrainDelegate) {
            me.constrainTo = me.calculateConstrainRegion();
        }

        if (comp.beginDrag) {
            comp.beginDrag();
        }
    },

    calculateConstrainRegion: function() {
        var me = this,
            comp = me.comp,
            constrainTo = me.initialConstrainTo,
            constraintInsets = comp.constraintInsets,
            constrainEl,
            delegateRegion,
            elRegion,
            dragEl = me.proxy ? me.proxy.el : comp.el,
            shadowSize = (!me.constrainDelegate && dragEl.shadow && comp.constrainShadow && !dragEl.shadowDisabled) ? dragEl.shadow.getShadowSize() : 0;

        
        if (!(constrainTo instanceof Ext.util.Region)) {
            constrainEl = Ext.fly(constrainTo);
            constrainTo =  constrainEl.getViewRegion();

            
            constrainTo.right = constrainTo.left + constrainEl.dom.clientWidth;
        } else {
            
            constrainTo = constrainTo.copy();
        }

        
        if (constraintInsets) {
            constraintInsets = Ext.isObject(constraintInsets) ? constraintInsets : Ext.Element.parseBox(constraintInsets);
            constrainTo.adjust(constraintInsets.top, constraintInsets.right, constraintInsets.bottom, constraintInsets.length);
        }

        
        if (shadowSize) {
            constrainTo.adjust(shadowSize[0], -shadowSize[1], -shadowSize[2], shadowSize[3]);
        }

        
        
        
        if (!me.constrainDelegate) {
            delegateRegion = Ext.fly(me.dragTarget).getRegion();
            elRegion = dragEl.getRegion();

            constrainTo.adjust(
                delegateRegion.top - elRegion.top,
                delegateRegion.right - elRegion.right,
                delegateRegion.bottom - elRegion.bottom,
                delegateRegion.left - elRegion.left
            );
        }
        return constrainTo;
    },

    
    onDrag: function(e) {
        var me = this,
            comp = (me.proxy && !me.comp.liveDrag) ? me.proxy : me.comp,
            offset = me.getOffset(me.constrain || me.constrainDelegate ? 'dragTarget' : null);

        comp.setPagePosition(me.startPosition[0] + offset[0], me.startPosition[1] + offset[1]);
    },

    onEnd: function(e) {
        var comp = this.comp;
        if (comp.isDestroyed || comp.destroying) {
            return;
        }
        
        if (this.proxy && !comp.liveDrag) {
            comp.unghost();
        }
        if (comp.endDrag) {
            comp.endDrag();
        }
    }
});


Ext.define('Ext.window.Window', {
    extend:  Ext.panel.Panel ,

    alternateClassName: 'Ext.Window',

                                                                                   

    alias: 'widget.window',

    

    

    

    

    

    

    

    

    

    

    
    baseCls: Ext.baseCSSPrefix + 'window',

    
    resizable: true,

    
    draggable: true,

    
    constrain: false,

    
    constrainHeader: false,

    

    
    plain: false,

    
    minimizable: false,

    
    maximizable: false,

    
    minHeight: 50,

    
    minWidth: 50,

    
    expandOnShow: true,

    
    collapsible: false,

    
    closable: true,

    
    hidden: true,

    
    autoRender: true,

    
    hideMode: 'offsets',

    
    floating: true,

    itemCls: Ext.baseCSSPrefix + 'window-item',

    initialAlphaNum: /^[a-z0-9]/,

    overlapHeader: true,

    ignoreHeaderBorderManagement: true,

    
    alwaysFramed: true,

    
    isRootCfg: {
        isRoot: true
    },

    
    isWindow: true,
    
    ariaRole: 'dialog',

    
    initComponent: function() {
        var me = this;
        
        
        me.frame = false;
        me.callParent();
        me.addEvents(
            

            

            
            'resize',

            
            'maximize',

            
            'minimize',

            
            'restore'
        );

        if (me.plain) {
            me.addClsWithUI('plain');
        }

        me.addStateEvents(['maximize', 'restore', 'resize', 'dragend']);
    },

    getElConfig: function () {
        var me = this,
            elConfig;

        elConfig = me.callParent();
        elConfig.tabIndex = -1;
        return elConfig;
    },

    

    
    getState: function() {
        var me = this,
            state = me.callParent() || {},
            maximized = !!me.maximized,
            ghostBox = me.ghostBox,
            pos;


        state.maximized = maximized;
        if (maximized) {
            pos = me.restorePos;
        } else if (ghostBox) {
            
            
            pos = [ghostBox.x, ghostBox.y];
        } else {
            pos = me.getPosition();
        }
        Ext.apply(state, {
            size: maximized ? me.restoreSize : me.getSize(),
            pos: pos
        });
        return state;
    },

    applyState: function(state){
        var me = this;

        if (state) {
            me.maximized = state.maximized;
            if (me.maximized) {
                me.hasSavedRestore = true;
                me.restoreSize = state.size;
                me.restorePos = state.pos;
            } else {
                Ext.apply(me, {
                    width: state.size.width,
                    height: state.size.height,
                    x: state.pos[0],
                    y: state.pos[1]
                });
            }
        }
    },

    
    onRender: function(ct, position) {
        var me = this;
        me.callParent(arguments);
        me.focusEl = me.el;

        
        if (me.maximizable) {
            me.header.on({
                scope: me,
                dblclick: me.toggleMaximize
            });
        }
    },

    
    afterRender: function() {
        var me = this,
            header = me.header,
            keyMap;

        me.callParent();

        
        if (me.maximized) {
            me.maximized = false;
            me.maximize();
            if (header) {
                header.removeCls(header.indicateDragCls)
            }
        }

        if (me.closable) {
            keyMap = me.getKeyMap();
            keyMap.on(27, me.onEsc, me);
        } else {
            keyMap = me.keyMap;
        }
        if (keyMap && me.hidden) {
            keyMap.disable();
        }
    },

    
    
    initDraggable: function() {
        
        this.initSimpleDraggable();
    },

    initResizable: function(){
        this.callParent(arguments);
        if (this.maximized) {
            this.resizer.disable();
        }
    },

    
    onEsc: function(k, e) {
        
        if (!Ext.FocusManager || !Ext.FocusManager.enabled || Ext.FocusManager.focusedCmp === this) {
            e.stopEvent();
            this.close();
        }
    },

    
    beforeDestroy: function() {
        var me = this;
        if (me.rendered) {
            delete this.animateTarget;
            me.hide();
            Ext.destroy(
                me.keyMap
            );
        }
        me.callParent();
    },

    
    addTools: function() {
        var me = this;

        
        me.callParent();

        if (me.minimizable) {
            me.addTool({
                type: 'minimize',
                handler: Ext.Function.bind(me.minimize, me, [])
            });
        }
        if (me.maximizable) {
            me.addTool({
                type: 'maximize',
                handler: Ext.Function.bind(me.maximize, me, [])
            });
            me.addTool({
                type: 'restore',
                handler: Ext.Function.bind(me.restore, me, []),
                hidden: true
            });
        }
    },

    
    getFocusEl: function() {
        return this.getDefaultFocus();
    },

    
    getDefaultFocus: function() {
        var me = this,
            result,
            defaultComp = me.defaultButton || me.defaultFocus,
            selector;

        if (defaultComp !== undefined) {
            
            if (Ext.isNumber(defaultComp)) {
                result = me.query('button')[defaultComp];
            }
            
            else if (Ext.isString(defaultComp)) {
                selector = defaultComp;

                
                if (selector.match(me.initialAlphaNum)) {
                    result = me.down('#' + selector);
                }
                
                if (!result) {
                    result = me.down(selector);
                }
            }
            
            else if (defaultComp.focus) {
                result = defaultComp;
            }
        }
        return result || me.el;
    },

    
    onFocus: function() {
        var me = this,
            focusDescendant;

        
        if ((Ext.FocusManager && Ext.FocusManager.enabled) || ((focusDescendant = me.getDefaultFocus()) === me)) {
            me.callParent(arguments);
        } else {
            focusDescendant.focus();
        }
    },

    onShow: function() {
        var me = this;

        me.callParent(arguments);
        if (me.expandOnShow) {
            me.expand(false);
        }
        me.syncMonitorWindowResize();

        if (me.keyMap) {
            me.keyMap.enable();
        }
   },

    
    doClose: function() {
        var me = this;

        
        if (me.hidden) {
            me.fireEvent('close', me);
            if (me.closeAction == 'destroy') {
                this.destroy();
            }
        } else {
            
            me.hide(me.animateTarget, me.doClose, me);
        }
    },

    
    afterHide: function() {
        var me = this;

        
        me.syncMonitorWindowResize();

        
        if (me.keyMap) {
            me.keyMap.disable();
        }

        
        me.callParent(arguments);
    },

    
    onWindowResize: function() {
        var me = this,
            sizeModel;

        if (me.maximized) {
            me.fitContainer();
        } else {
            sizeModel = me.getSizeModel();
            if (sizeModel.width.natural || sizeModel.height.natural) {
                me.updateLayout();
            }
            me.doConstrain();
        }

    },

    
    minimize: function() {
        this.fireEvent('minimize', this);
        return this;
    },

    resumeHeaderLayout: function(changed) {
        this.header.resumeLayouts(changed ? this.isRootCfg : null);
    },

    afterCollapse: function() {
        var me = this,
            header = me.header,
            tools = me.tools;

        if (header && me.maximizable) {
            header.suspendLayouts();
            tools.maximize.hide();
            tools.restore.hide();
            this.resumeHeaderLayout(true);
        }
        if (me.resizer) {
            me.resizer.disable();
        }
        me.callParent(arguments);
    },

    afterExpand: function() {
        var me = this,
            header = me.header,
            tools = me.tools,
            changed;


        if (header) {
            header.suspendLayouts();
            if (me.maximized) {
                tools.restore.show();
                changed = true;
            } else if (me.maximizable) {
                tools.maximize.show();
                changed = true;
            }
            this.resumeHeaderLayout(changed);
        }
        if (me.resizer) {
            me.resizer.enable();
        }
        me.callParent(arguments);
    },

    
    maximize: function(animate) {
        var me = this,
            header = me.header,
            tools = me.tools,
            width = me.width,
            height = me.height,
            restore, changed;

        if (!me.maximized) {
            me.expand(false);
            if (!me.hasSavedRestore) {
                restore = me.restoreSize = {
                    width: Ext.isNumber(width) ? width : null,
                    height: Ext.isNumber(height) ? height : null
                };
                
                me.restorePos = me.getPosition(true);
            }

            
            if (header) {
                header.suspendLayouts();
                if (tools.maximize) {
                    tools.maximize.hide();
                    changed = true;
                }
                if (tools.restore) {
                    tools.restore.show();
                    changed = true;
                }
                if (me.collapseTool) {
                    me.collapseTool.hide();
                    changed = true;
                }
                me.resumeHeaderLayout(changed);
            }

            me.el.disableShadow();

            if (me.dd) {
                me.dd.disable();
                if (header) {
                   header.removeCls(header.indicateDragCls)
                }
            }
            if (me.resizer) {
                me.resizer.disable();
            }

            me.el.addCls(Ext.baseCSSPrefix + 'window-maximized');
            me.container.addCls(Ext.baseCSSPrefix + 'window-maximized-ct');

            me.syncMonitorWindowResize();
            me.fitContainer(animate = (animate || !!me.animateTarget) ? {
                callback: function() {
                    me.maximized = true;
                    me.fireEvent('maximize', me);
                }
            } : null);
            if (!animate) {
                me.maximized = true;
                me.fireEvent('maximize', me);
            }
        }
        return me;
    },

    
    restore: function(animate) {
        var me = this,
            tools = me.tools,
            header = me.header,
            newBox = me.restoreSize,
            changed;

        if (me.maximized) {
            me.hasSavedRestore = null;
            me.removeCls(Ext.baseCSSPrefix + 'window-maximized');

            
            if (header) {
                header.suspendLayouts();
                if (tools.restore) {
                    tools.restore.hide();
                    changed = true;
                }
                if (tools.maximize) {
                    tools.maximize.show();
                    changed = true;
                }
                if (me.collapseTool) {
                    me.collapseTool.show();
                    changed = true;
                }
                me.resumeHeaderLayout(changed);
            }

            
            newBox.x = me.restorePos[0];
            newBox.y = me.restorePos[1];
            me.setBox(newBox, animate = (animate || !!me.animateTarget) ? {
                callback: function() {
                    me.el.enableShadow(true);
                    me.maximized = false;
                    me.fireEvent('restore', me);
                }
            } : null);

            
            me.restorePos = me.restoreSize = null;

            
            if (me.dd) {
                me.dd.enable();
                if (header) {
                    header.addCls(header.indicateDragCls)
                }
            }

            if (me.resizer) {
                me.resizer.enable();
            }

            me.container.removeCls(Ext.baseCSSPrefix + 'window-maximized-ct');

            me.syncMonitorWindowResize();

            if (!animate) {
                me.el.enableShadow(true);
                me.maximized = false;
                me.fireEvent('restore', me);
            }
        }
        return me;
    },

    
    syncMonitorWindowResize: function () {
        var me = this,
            currentlyMonitoring = me._monitoringResize,
            
            yes = me.monitorResize || me.constrain || me.constrainHeader || me.maximized,
            
            veto = me.hidden || me.destroying || me.isDestroyed;

        if (yes && !veto) {
            
            if (!currentlyMonitoring) {
                
                
                Ext.EventManager.onWindowResize(me.onWindowResize, me, {delay: 1});
                me._monitoringResize = true;
            }
        } else if (currentlyMonitoring) {
            
            Ext.EventManager.removeResizeListener(me.onWindowResize, me);
            me._monitoringResize = false;
        }
    },

    
    toggleMaximize: function() {
        return this[this.maximized ? 'restore': 'maximize']();
    },

    createGhost: function() {
        var ghost = this.callParent(arguments);
        ghost.xtype = 'window';
        return ghost;
    }

});


Ext.define("Ext.form.Labelable", {
                                
    
    isLabelable: true,

    autoEl: {
        tag: 'table',
        cellpadding: 0,
        role: 'presentation'
    },

    childEls: [
        
        'labelCell',

        
        'labelEl',

        
        'bodyEl',

        
        'sideErrorCell',

        
        'errorEl',

        'inputRow'
    ],

    
    labelableRenderTpl: [

        
        '<tr role="presentation" id="{id}-inputRow" <tpl if="inFormLayout">id="{id}"</tpl> class="{inputRowCls}">',

            
            '<tpl if="labelOnLeft">',
                '<td role="presentation" id="{id}-labelCell" style="{labelCellStyle}" {labelCellAttrs}>',
                    '{beforeLabelTpl}',
                    '<label id="{id}-labelEl" {labelAttrTpl}',
                        '<tpl if="inputId && !(boxLabel && !fieldLabel)"> for="{inputId}"</tpl>',
                        ' class="{labelCls}"',
                        '<tpl if="labelStyle"> style="{labelStyle}"</tpl>',
                        
                        ' unselectable="on"',
                    '>',
                        '{beforeLabelTextTpl}',
                        '<tpl if="fieldLabel">{fieldLabel}',
                            '<tpl if="labelSeparator">',
                                '<span role="separator">{labelSeparator}</span>',
                            '</tpl>',
                        '</tpl>',
                        '{afterLabelTextTpl}',
                    '</label>',
                    '{afterLabelTpl}',
                '</td>',
            '</tpl>',

            
            '<td role="presentation" class="{baseBodyCls} {fieldBodyCls} {extraFieldBodyCls}" id="{id}-bodyEl" colspan="{bodyColspan}" role="presentation">',
                '{beforeBodyEl}',

                
                '<tpl if="labelAlign==\'top\'">',
                    '{beforeLabelTpl}',
                    '<div role="presentation" id="{id}-labelCell" style="{labelCellStyle}">',
                        '<label id="{id}-labelEl" {labelAttrTpl}<tpl if="inputId"> for="{inputId}"</tpl> class="{labelCls}"',
                            '<tpl if="labelStyle"> style="{labelStyle}"</tpl>',
                            
                            ' unselectable="on"',
                        '>',
                            '{beforeLabelTextTpl}',
                            '<tpl if="fieldLabel">{fieldLabel}',
                                '<tpl if="labelSeparator">',
                                    '<span role="separator">{labelSeparator}</span>',
                                '</tpl>',
                            '</tpl>',
                            '{afterLabelTextTpl}',
                        '</label>',
                    '</div>',
                    '{afterLabelTpl}',
                '</tpl>',

                '{beforeSubTpl}',
                '{[values.$comp.getSubTplMarkup(values)]}',
                '{afterSubTpl}',

            
            '<tpl if="msgTarget===\'side\'">',
                '{afterBodyEl}',
                '</td>',
                '<td role="presentation" id="{id}-sideErrorCell" vAlign="{[values.labelAlign===\'top\' && !values.hideLabel ? \'bottom\' : \'middle\']}" style="{[values.autoFitErrors ? \'display:none\' : \'\']}" width="{errorIconWidth}">',
                    '<div role="alert" aria-live="polite" id="{id}-errorEl" class="{errorMsgCls}" style="display:none"></div>',
                '</td>',
            '<tpl elseif="msgTarget==\'under\'">',
                '<div role="alert" aria-live="polite" id="{id}-errorEl" class="{errorMsgClass}" colspan="2" style="display:none"></div>',
                '{afterBodyEl}',
                '</td>',
            '</tpl>',
        '</tr>',
        {
            disableFormats: true
        }
    ],

    
    activeErrorsTpl: undefined,

    htmlActiveErrorsTpl: [
        '<tpl if="errors && errors.length">',
            '<ul class="{listCls}">',
                '<tpl if="Ext.enableAria">',
                    '<tpl if="fieldLabel"><div>{fieldLabel}</div></tpl>',
                '</tpl>',
                '<tpl for="errors"><li>{.}</li></tpl>',
            '</ul>',
        '</tpl>'
    ],
    
    plaintextActiveErrorsTpl: [
        '<tpl if="errors && errors.length">',
            '<tpl if="Ext.enableAria">',
                '<tpl if="fieldLabel">{fieldLabel}\n</tpl>',
            '</tpl>',
            '<tpl for="errors"><tpl if="xindex &gt; 1">\n</tpl>{.}</tpl>',
        '</tpl>'
    ],

    
    isFieldLabelable: true,

    
    formItemCls: Ext.baseCSSPrefix + 'form-item',

    
    labelCls: Ext.baseCSSPrefix + 'form-item-label',

    

    
    errorMsgCls: Ext.baseCSSPrefix + 'form-error-msg',

    
    baseBodyCls: Ext.baseCSSPrefix + 'form-item-body',

    
    inputRowCls: Ext.baseCSSPrefix + 'form-item-input-row',

    
    fieldBodyCls: '',

    
    clearCls: Ext.baseCSSPrefix + 'clear',

    
    invalidCls : Ext.baseCSSPrefix + 'form-invalid',

    
    fieldLabel: undefined,

    
    labelAlign : 'left',

    
    labelWidth: 100,

    
    labelPad : 5,

    
    
    labelSeparator : ':',
    

    

    
    hideLabel: false,

    
    hideEmptyLabel: true,

    
    preventMark: false,

    
    autoFitErrors: true,

    
    msgTarget: 'qtip',

    

    
    noWrap: true,

    labelableInsertions: [

        
        'beforeBodyEl',

        
        'afterBodyEl',

        
        'beforeLabelTpl',

        
        'afterLabelTpl',

        
        'beforeSubTpl',

        
        'afterSubTpl',

        
        'beforeLabelTextTpl',

        
        'afterLabelTextTpl',

        
        'labelAttrTpl'
    ],

    
    labelableRenderProps: ['allowBlank', 'id', 'labelAlign', 'fieldBodyCls', 'extraFieldBodyCls', 
        'baseBodyCls', 'clearCls', 'labelSeparator', 'msgTarget', 'inputRowCls'],

    
    initLabelable: function() {
        var me = this,
            padding = me.padding;

        
        
        
        if (padding) {
            me.padding = undefined;
            me.extraMargins = Ext.Element.parseBox(padding);
        }
        
        if (!me.activeErrorsTpl) {
            if (me.msgTarget == 'title') {
                me.activeErrorsTpl = me.plaintextActiveErrorsTpl;
            } else {
                me.activeErrorsTpl = me.htmlActiveErrorsTpl;
            }
        }

        me.addCls(Ext.plainTableCls);
        me.addCls(me.formItemCls);
        
        
        me.lastActiveError = '';

        me.addEvents(
            
            'errorchange'
        );

        
        me.enableBubble('errorchange');
    },

    
    trimLabelSeparator: function() {
        var me = this,
            separator = me.labelSeparator,
            label = me.fieldLabel || '',
            lastChar = label.substr(label.length - 1);

        
        return lastChar === separator ? label.slice(0, -1) : label;
    },

    
    getFieldLabel: function() {
        return this.trimLabelSeparator();
    },
    
    
    setFieldLabel: function(label){
        label = label || '';
        
        var me = this,
            separator = me.labelSeparator,
            labelEl = me.labelEl;
        
        me.fieldLabel = label;
        if (me.rendered) {
            if (Ext.isEmpty(label) && me.hideEmptyLabel) {
                labelEl.parent().setDisplayed('none');
            } else {
                if (separator) {
                    label = me.trimLabelSeparator() + separator;
                }
                labelEl.update(label);
                labelEl.parent().setDisplayed('');
            }
            me.updateLayout();
        }
    },

    getInsertionRenderData: function (data, names) {
        var i = names.length,
            name, value;

        while (i--) {
            name = names[i];
            value = this[name];

            if (value) {
                if (typeof value != 'string') {
                    if (!value.isTemplate) {
                        value = Ext.XTemplate.getTpl(this, name);
                    }
                    value = value.apply(data);
                }
            }

            data[name] = value || '';
        }

        return data;
    },

    
    getLabelableRenderData: function() {
        var me = this,
            data,
            tempEl,
            topLabel = me.labelAlign === 'top';

        if (!Ext.form.Labelable.errorIconWidth) {
            tempEl = Ext.getBody().createChild({style: 'position:absolute', cls: Ext.baseCSSPrefix + 'form-invalid-icon'});
            Ext.form.Labelable.errorIconWidth = tempEl.getWidth() + tempEl.getMargin('l');
            tempEl.remove();
        }

        data = Ext.copyTo({
            inFormLayout   : me.ownerLayout && me.ownerLayout.type === 'form',
            inputId        : me.getInputId(),
            labelOnLeft    : !topLabel,
            hideLabel      : !me.hasVisibleLabel(),
            fieldLabel     : me.getFieldLabel(),
            labelCellStyle : me.getLabelCellStyle(),
            labelCellAttrs : me.getLabelCellAttrs(),
            labelCls       : me.getLabelCls(),
            labelStyle     : me.getLabelStyle(),
            bodyColspan    : me.getBodyColspan(),
            externalError  : !me.autoFitErrors,
            errorMsgCls    : me.getErrorMsgCls(),
            errorIconWidth : Ext.form.Labelable.errorIconWidth,
            boxLabel       : me.boxLabel
        },
        me, me.labelableRenderProps, true);

        me.getInsertionRenderData(data, me.labelableInsertions);

        return data;
    },

    xhooks: {
        beforeRender: function() {
            var me = this;
            me.setFieldDefaults(me.getHierarchyState().fieldDefaults);
            if (me.ownerLayout) {
                me.addCls(Ext.baseCSSPrefix + me.ownerLayout.type + '-form-item');
            }
        },

        onRender: function() {
            var me = this,
                margins,
                side,
                style = {};

            if (me.extraMargins) {
                margins = me.el.getMargin();
                for (side in margins) {
                    if (margins.hasOwnProperty(side)) {
                        style['margin-' + side] = (margins[side] + me.extraMargins[side]) + 'px';
                    }
                }
                me.el.setStyle(style);
            }
        }
    },
    
    
    hasVisibleLabel: function(){
        if (this.hideLabel) {
            return false;
        }
        return !(this.hideEmptyLabel && !this.getFieldLabel());
    },
    
    
    getLabelWidth: function(){
        var me = this;
        if (!me.hasVisibleLabel()) {
            return 0;
        }
        return me.labelWidth + me.labelPad;
    },
    
    
    getBodyColspan: function() {
        var me = this,
            result;

        if (me.msgTarget === 'side' && (!me.autoFitErrors || me.hasActiveError())) {
            result = 1;
        } else {
            result = 2;
        }
        if (me.labelAlign !== 'top' && !me.hasVisibleLabel()) {
            result++;
        }
        return result;
    },
    
    getLabelCls: function() {
        var labelCls = this.labelCls + ' ' + Ext.dom.Element.unselectableCls,
            labelClsExtra = this.labelClsExtra;

        return labelClsExtra ? labelCls + ' ' + labelClsExtra : labelCls;
    },

    getLabelCellStyle: function() {
        var me = this,
            hideLabelCell = me.hideLabel || (!me.getFieldLabel() && me.hideEmptyLabel);

        return hideLabelCell ? 'display:none;' : '';
    },
    
    getErrorMsgCls: function() {
        var me = this,
            hideLabelCell = (me.hideLabel || (!me.fieldLabel && me.hideEmptyLabel));
        
        return me.errorMsgCls + (!hideLabelCell && me.labelAlign === 'top' ? ' ' + Ext.baseCSSPrefix + 'lbl-top-err-icon' : '');
    },

    getLabelCellAttrs: function() {
        var me = this,
            labelAlign = me.labelAlign,
            result = '';

        if (labelAlign !== 'top') {
            result = 'valign="top" halign="' + labelAlign + '" width="' + (me.labelWidth + me.labelPad) + '"';
        }
        return result + ' class="' + Ext.baseCSSPrefix + 'field-label-cell"';
    },
    
    
    getLabelStyle: function(){
        var me = this,
            labelPad = me.labelPad,
            labelStyle = '';

        
        
        if (me.labelAlign !== 'top') {
            if (me.labelWidth) {
                labelStyle = 'width:' + me.labelWidth + 'px;';
            }
            if (labelPad) {
                labelStyle += 'margin-right:' + labelPad + 'px;';
            }
        }
        
        return labelStyle + (me.labelStyle || '');
    },

    
    getSubTplMarkup: function() {
        return '';
    },

    
    getInputId: function() {
        return '';
    },

    
    getActiveError : function() {
        return this.activeError || '';
    },

    
    hasActiveError: function() {
        return !!this.getActiveError();
    },

    
    setActiveError: function(msg) {
        this.setActiveErrors(msg);
    },

    
    getActiveErrors: function() {
        return this.activeErrors || [];
    },

    
    setActiveErrors: function(errors) {
        var me = this,
            tpl;
        
        errors = Ext.Array.from(errors);
        tpl = me.getTpl('activeErrorsTpl');
        
        me.activeErrors = errors;
        me.activeError = tpl.apply({
            fieldLabel: me.fieldLabel,
            errors: errors,
            listCls: Ext.plainListCls 
        });
        
        me.renderActiveError();
    },

    
    unsetActiveError: function() {
        delete this.activeError;
        delete this.activeErrors;
        this.renderActiveError();
    },

    
    renderActiveError: function() {
        var me = this,
            activeError = me.getActiveError(),
            hasError = !!activeError;

        if (activeError !== me.lastActiveError) {
            me.fireEvent('errorchange', me, activeError);
            me.lastActiveError = activeError;
        }

        if (me.rendered && !me.isDestroyed && !me.preventMark) {
            
            me.el[hasError ? 'addCls' : 'removeCls'](me.invalidCls);

            
            if (me.errorEl) {
                me.errorEl.dom.innerHTML = activeError;
            }
        }
    },

    
    setFieldDefaults: function(defaults) {
        var key;

        for (key in defaults) {
            if (!this.hasOwnProperty(key)) {
                this[key] = defaults[key];
            }
        }
    }
});


Ext.define('Ext.form.field.Field', {
    
    isFormField : true,

    

    

    
    disabled : false,

    
    submitValue: true,

    
    validateOnChange: true,

    
    suspendCheckChange: 0,

    
    initField: function() {
        this.addEvents(
            
            'change',
            
            'validitychange',
            
            'dirtychange'
        );

        this.initValue();
        
    },

    
    initValue: function() {
        var me = this;

        me.value = me.transformOriginalValue(me.value);
        
        me.originalValue = me.lastValue = me.value;

        
        me.suspendCheckChange++;
        me.setValue(me.value);
        me.suspendCheckChange--;
    },
    
    
    transformOriginalValue: Ext.identityFn,

    
    getName: function() {
        return this.name;
    },

    
    getValue: function() {
        return this.value;
    },

    
    setValue: function(value) {
        var me = this;
        me.value = value;
        me.checkChange();
        return me;
    },

    
    isEqual: function(value1, value2) {
        return String(value1) === String(value2);
    },

    
    isEqualAsString: function(value1, value2){
        return String(Ext.value(value1, '')) === String(Ext.value(value2, ''));
    },

    
    getSubmitData: function() {
        var me = this,
            data = null;
        if (!me.disabled && me.submitValue) {
            data = {};
            data[me.getName()] = '' + me.getValue();
        }
        return data;
    },

    
    getModelData: function(includeEmptyText,  isSubmitting) {
        var me = this,
            data = null;
        
        
        
        if (!me.disabled && (me.submitValue || !isSubmitting)) {
            data = {};
            data[me.getName()] = me.getValue();
        }
        return data;
    },

    
    reset : function(){
        var me = this;

        me.beforeReset();
        me.setValue(me.originalValue);
        me.clearInvalid();
        
        delete me.wasValid;
    },
    
    
    beforeReset: Ext.emptyFn,

    
    resetOriginalValue: function() {
        this.originalValue = this.getValue();
        this.checkDirty();
    },

    
    checkChange: function() {
        var me = this,
            newVal, oldVal;
            
        if (!me.suspendCheckChange) {
            newVal = me.getValue();
            oldVal = me.lastValue;
                
            if (!me.isDestroyed && me.didValueChange(newVal, oldVal)) {
                me.lastValue = newVal;
                me.fireEvent('change', me, newVal, oldVal);
                me.onChange(newVal, oldVal);
            }
        }
    },
    
    
    didValueChange: function(newVal, oldVal){
        return !this.isEqual(newVal, oldVal);
    },

    
    onChange: function(newVal, oldVal) {
        if (this.validateOnChange) {
            this.validate();
        }
        this.checkDirty();
    },

    
    isDirty : function() {
        var me = this;
        return !me.disabled && !me.isEqual(me.getValue(), me.originalValue);
    },

    
    checkDirty: function() {
        var me = this,
            isDirty = me.isDirty();
        if (isDirty !== me.wasDirty) {
            me.fireEvent('dirtychange', me, isDirty);
            me.onDirtyChange(isDirty);
            me.wasDirty = isDirty;
        }
    },

    
    onDirtyChange: Ext.emptyFn,

    
    getErrors: function(value) {
        return [];
    },

    
    isValid : function() {
        var me = this;
        return me.disabled || Ext.isEmpty(me.getErrors());
    },

    
    validate : function() {
        var me = this,
            isValid = me.isValid();
        if (isValid !== me.wasValid) {
            me.wasValid = isValid;
            me.fireEvent('validitychange', me, isValid);
        }
        return isValid;
    },

    
    batchChanges: function(fn) {
        try {
            this.suspendCheckChange++;
            fn();
        } catch(e){
            throw e;
        } finally {
            this.suspendCheckChange--;
        }
        this.checkChange();
    },

    
    isFileUpload: function() {
        return false;
    },

    
    extractFileInput: function() {
        return null;
    },

    
    markInvalid: Ext.emptyFn,

    
    clearInvalid: Ext.emptyFn

});


Ext.define('Ext.layout.component.field.Field', {

    

    extend:  Ext.layout.component.Auto ,

    alias: 'layout.field',

                                                                       

    

    type: 'field',

    naturalSizingProp: 'size',

    beginLayout: function(ownerContext) {
        var me = this,
            owner = me.owner;

        me.callParent(arguments);

        ownerContext.labelStrategy = me.getLabelStrategy();
        ownerContext.errorStrategy = me.getErrorStrategy();

        ownerContext.labelContext = ownerContext.getEl('labelEl');
        ownerContext.bodyCellContext = ownerContext.getEl('bodyEl');
        ownerContext.inputContext = ownerContext.getEl('inputEl');
        ownerContext.errorContext = ownerContext.getEl('errorEl');

        
        
        if (Ext.isIE7m && Ext.isStrict && ownerContext.inputContext) {
            me.ieInputWidthAdjustment = ownerContext.inputContext.getPaddingInfo().width + ownerContext.inputContext.getBorderInfo().width;
        }

        
        ownerContext.labelStrategy.prepare(ownerContext, owner);
        ownerContext.errorStrategy.prepare(ownerContext, owner);
    },
    
    beginLayoutCycle: function(ownerContext){
        var me = this,
            owner = me.owner,
            widthModel = ownerContext.widthModel,
            ownerNaturalSize = owner[me.naturalSizingProp],
            width;
            
        me.callParent(arguments);
        
        if (widthModel.shrinkWrap) {
            
            me.beginLayoutShrinkWrap(ownerContext);
        } else if (widthModel.natural) {

            
            if (typeof ownerNaturalSize == 'number' && !owner.inputWidth) {
                me.beginLayoutFixed(ownerContext, (width = ownerNaturalSize * 6.5 + 20), 'px');
            }

            
            else {
                me.beginLayoutShrinkWrap(ownerContext);
            }
            ownerContext.setWidth(width, false);
        } else {
            me.beginLayoutFixed(ownerContext, '100', '%');
        }    
    },

    beginLayoutFixed: function (ownerContext, width, suffix) {
        var owner = ownerContext.target,
            inputEl = owner.inputEl,
            inputWidth = owner.inputWidth;

        owner.el.setStyle('table-layout', 'fixed');
        owner.bodyEl.setStyle('width', width + suffix);
        if (inputEl) {
            if (inputWidth) {
                inputEl.setStyle('width', inputWidth + 'px');
            } else {
                inputEl.setStyle('width', owner.stretchInputElFixed ? '100%' : '');
            }
        }
        ownerContext.isFixed = true;
    },

    beginLayoutShrinkWrap: function (ownerContext) {
        var owner = ownerContext.target,
            inputEl = owner.inputEl,
            inputWidth = owner.inputWidth;

        if (inputEl && inputEl.dom) {
            inputEl.dom.removeAttribute('size');
            if (inputWidth) {
                inputEl.setStyle('width', inputWidth + 'px');
            } else {
                inputEl.setStyle('width', '');
            }
        }
        owner.el.setStyle('table-layout', 'auto');
        owner.bodyEl.setStyle('width', '');
    },

    finishedLayout: function(ownerContext){
        var owner = this.owner;

        this.callParent(arguments);        
        ownerContext.labelStrategy.finishedLayout(ownerContext, owner);
        ownerContext.errorStrategy.finishedLayout(ownerContext, owner);
    },

    calculateOwnerHeightFromContentHeight: function(ownerContext, contentHeight) {
        return contentHeight;
    },

    measureContentHeight: function (ownerContext) {
        return ownerContext.el.getHeight();
    },
    
    measureContentWidth: function (ownerContext) {
        return ownerContext.el.getWidth();
    },

    measureLabelErrorHeight: function (ownerContext) {
        return ownerContext.labelStrategy.getHeight(ownerContext) +
               ownerContext.errorStrategy.getHeight(ownerContext);
    },

    onFocus: function() {
        this.getErrorStrategy().onFocus(this.owner);    
    },

    
    getLabelStrategy: function() {
        var me = this,
            strategies = me.labelStrategies,
            labelAlign = me.owner.labelAlign;
        return strategies[labelAlign] || strategies.base;
    },

    
    getErrorStrategy: function() {
        var me = this,
            owner = me.owner,
            strategies = me.errorStrategies,
            msgTarget = owner.msgTarget;
        return !owner.preventMark && Ext.isString(msgTarget) ?
                (strategies[msgTarget] || strategies.elementId) :
                strategies.none;
    },

    
    labelStrategies: (function() {
        var base = {
                prepare: function(ownerContext, owner) {
                    var cls = owner.labelCls + '-' + owner.labelAlign,
                        labelEl = owner.labelEl;

                    if (labelEl) {
                        labelEl.addCls(cls);
                    }
                },

                getHeight: function () {
                    return 0;
                },
                
                finishedLayout: Ext.emptyFn
            };

        return {
            base: base,

            
            top: Ext.applyIf({        
                        
                getHeight: function (ownerContext) {
                    var labelContext = ownerContext.labelContext,
                        props = labelContext.props,
                        height = props.height;
                        
                    if (height === undefined) {
                        props.height = height = labelContext.el.getHeight() + labelContext.getMarginInfo().height;
                    }

                    return height;
                }
            }, base),

            
            left: base,

            
            right: base
        };
    }()),

    
    errorStrategies: (function() {
        function showTip(owner) {
            var tip = Ext.layout.component.field.Field.tip,
                target;
                
            if (tip && tip.isVisible()) {
                target = tip.activeTarget;
                if (target && target.el === owner.getActionEl().dom) {
                    tip.toFront(true);
                }
            }
        }

        var applyIf = Ext.applyIf,
            emptyFn = Ext.emptyFn,
            iconCls = Ext.baseCSSPrefix + 'form-invalid-icon',
            iconWidth,
            base = {
                prepare: function(ownerContext, owner) {
                    var el = owner.errorEl;
                    if (el) {
                        el.setDisplayed(false);
                    }
                },
                getHeight: function () {
                    return 0;
                },
                onFocus: emptyFn,
                finishedLayout: emptyFn
            };

        return {
            none: base,

            
            side: applyIf({
                prepare: function(ownerContext, owner) {
                    var errorEl = owner.errorEl,
                        sideErrorCell = owner.sideErrorCell,
                        displayError = owner.hasActiveError(),
                        tempEl;

                    
                    if (!iconWidth) {
                        iconWidth = (tempEl = Ext.getBody().createChild({style: 'position:absolute', cls: iconCls})).getWidth();
                        tempEl.remove();
                    }

                    errorEl.addCls(iconCls);
                    errorEl.set({'data-errorqtip': owner.getActiveError() || ''});
                    if (owner.autoFitErrors) {
                        errorEl.setDisplayed(displayError);
                    }
                    
                    else {
                        errorEl.setVisible(displayError);
                    }

                    
                    if (sideErrorCell && owner.autoFitErrors) {
                        sideErrorCell.setDisplayed(displayError);
                    }
                    owner.bodyEl.dom.colSpan = owner.getBodyColspan();

                    
                    Ext.layout.component.field.Field.initTip();
                },
                onFocus: showTip
            }, base),

            
            under: applyIf({
                prepare: function(ownerContext, owner) {
                    var errorEl = owner.errorEl,
                        cls = Ext.baseCSSPrefix + 'form-invalid-under';

                    errorEl.addCls(cls);
                    errorEl.setDisplayed(owner.hasActiveError());
                },
                getHeight: function (ownerContext) {
                    var height = 0,
                        errorContext, props;

                    if (ownerContext.target.hasActiveError()) {
                        errorContext = ownerContext.errorContext;
                        props = errorContext.props;
                        height = props.height;

                        if (height === undefined) {
                            props.height = height = errorContext.el.getHeight();
                        }
                    }

                    return height;
                }
            }, base),

            
            qtip: applyIf({
                prepare: function(ownerContext, owner) {
                    Ext.layout.component.field.Field.initTip();
                    owner.getActionEl().dom.setAttribute('data-errorqtip', owner.getActiveError() || '');
                },
                onFocus: showTip
            }, base),

            
            title: applyIf({
                prepare: function(ownerContext, owner) {
                    owner.getActionEl().dom.setAttribute('title', owner.getActiveError() || '');
                }
            }, base),

            
            elementId: applyIf({
                prepare: function(ownerContext, owner) {
                    var targetEl = Ext.fly(owner.msgTarget);
                    if (targetEl) {
                        targetEl.dom.innerHTML = owner.getActiveError() || '';
                        targetEl.setDisplayed(owner.hasActiveError());
                    }
                }
            }, base)
        };
    }()),

    statics: {
        
        initTip: function() {
            var tip = this.tip;
            if (!tip) {
                tip = this.tip = Ext.create('Ext.tip.QuickTip', {
                    ui: 'form-invalid'
                });
                tip.tagConfig = Ext.apply({}, {attribute: 'errorqtip'}, tip.tagConfig);
            }
        },

        
        destroyTip: function() {
            var tip = this.tip;
            if (tip) {
                tip.destroy();
                delete this.tip;
            }
        }
    }
});


Ext.define('Ext.form.field.Base', {
    extend:  Ext.Component ,
    mixins: {
        labelable:  Ext.form.Labelable ,
        field:  Ext.form.field.Field 
    },
    alias: 'widget.field',
    alternateClassName: ['Ext.form.Field', 'Ext.form.BaseField'],
                                                                                            

    
    fieldSubTpl: [ 
        '<input id="{id}" type="{type}" role="{role}" {inputAttrTpl}',
            ' size="1"', 
            '<tpl if="name"> name="{name}"</tpl>',
            '<tpl if="value"> value="{[Ext.util.Format.htmlEncode(values.value)]}"</tpl>',
            '<tpl if="placeholder"> placeholder="{placeholder}"</tpl>',
            '{%if (values.maxLength !== undefined){%} maxlength="{maxLength}"{%}%}',
            '<tpl if="readOnly"> readonly="readonly"</tpl>',
            '<tpl if="disabled"> disabled="disabled"</tpl>',
            '<tpl if="tabIdx"> tabIndex="{tabIdx}"</tpl>',
            '<tpl if="fieldStyle"> style="{fieldStyle}"</tpl>',
        ' class="{fieldCls} {typeCls} {editableCls} {inputCls}" autocomplete="off"/>',
        {
            disableFormats: true
        }
    ],

    subTplInsertions: [
        
        'inputAttrTpl'
    ],

    

    
    inputType: 'text',

    

    
    
    invalidText : 'The value in this field is invalid',
    

    
    fieldCls : Ext.baseCSSPrefix + 'form-field',

    

    
    focusCls : 'form-focus',

    
    dirtyCls : Ext.baseCSSPrefix + 'form-dirty',

    
    checkChangeEvents: Ext.isIE && (!document.documentMode || document.documentMode < 9) ?
                        ['change', 'propertychange', 'keyup'] :
                        ['change', 'input', 'textInput', 'keyup', 'dragdrop'],

    
    checkChangeBuffer: 50,

    componentLayout: 'field',

    
    readOnly : false,

    
    readOnlyCls: Ext.baseCSSPrefix + 'form-readonly',

    

    
    validateOnBlur: true,

    
    hasFocus : false,

    baseCls: Ext.baseCSSPrefix + 'field',

    maskOnDisable: false,
    
    
    
    
    stretchInputElFixed: true,

    
    initComponent : function() {
        var me = this;

        me.callParent();

        me.subTplData = me.subTplData || {};

        me.addEvents(
            
            'specialkey',

            
            'writeablechange'
        );

        
        me.initLabelable();
        me.initField();

        
        if (!me.name) {
            me.name = me.getInputId();
        }
        
        if (me.readOnly) {
            me.addCls(me.readOnlyCls);
        }
        
        me.addCls(Ext.baseCSSPrefix + 'form-type-' + me.inputType);
    },

    
    getInputId: function() {
        return this.inputId || (this.inputId = this.id + '-inputEl');
    },

    
    getSubTplData: function() {
        var me = this,
            type = me.inputType,
            inputId = me.getInputId(),
            data;
        
        data = Ext.apply({
            id         : inputId,
            cmpId      : me.id,
            name       : me.name || inputId,
            disabled   : me.disabled,
            readOnly   : me.readOnly,
            value      : me.getRawValue(),
            type       : type,
            fieldCls   : me.fieldCls,
            fieldStyle : me.getFieldStyle(),
            tabIdx     : me.tabIndex,
            inputCls   : me.inputCls,
            typeCls    : Ext.baseCSSPrefix + 'form-' + (type === 'password' ? 'text' : type),
            role       : me.ariaRole
        }, me.subTplData);

        me.getInsertionRenderData(data, me.subTplInsertions);

        return data;
    },

    applyRenderSelectors: function() {
        var me = this;

        me.callParent();

        
        
        me.addChildEls('inputEl');

        
        me.inputEl = me.el.getById(me.getInputId());
    },

    
    getSubTplMarkup: function() {
        return this.getTpl('fieldSubTpl').apply(this.getSubTplData());
    },

    initRenderTpl: function() {
        var me = this;
        if (!me.hasOwnProperty('renderTpl')) {
            me.renderTpl = me.getTpl('labelableRenderTpl');
        }
        return me.callParent();
    },

    initRenderData: function() {
        return Ext.applyIf(this.callParent(), this.getLabelableRenderData());
    },

    
    setFieldStyle: function(style) {
        var me = this,
            inputEl = me.inputEl;
        if (inputEl) {
            inputEl.applyStyles(style);
        }
        me.fieldStyle = style;
    },

    getFieldStyle: function() {
        var style = this.fieldStyle;
        return Ext.isObject(style) ? Ext.DomHelper.generateStyles(style, null, true) : style || '';
    },

    
    onRender : function() {
        this.callParent(arguments);
        this.renderActiveError();
    },

    getFocusEl: function() {
        return this.inputEl;
    },

    isFileUpload: function() {
        return this.inputType === 'file';
    },

    
    getSubmitData: function() {
        var me = this,
            data = null,
            val;
        if (!me.disabled && me.submitValue) {
            val = me.getSubmitValue();
            if (val !== null) {
                data = {};
                data[me.getName()] = val;
            }
        }
        return data;
    },

    
    getSubmitValue: function() {
        return this.processRawValue(this.getRawValue());
    },

    
    getRawValue: function() {
        var me = this,
            v = (me.inputEl ? me.inputEl.getValue() : Ext.value(me.rawValue, ''));
        me.rawValue = v;
        return v;
    },

    
    setRawValue: function(value) {
        var me = this;
        value = Ext.value(me.transformRawValue(value), '');
        me.rawValue = value;

        
        if (me.inputEl) {
            me.inputEl.dom.value = value;
        }
        return value;
    },
    
    
    transformRawValue: Ext.identityFn,

    
    valueToRaw: function(value) {
        return '' + Ext.value(value, '');
    },

    
    rawToValue: Ext.identityFn,

    
    processRawValue: Ext.identityFn,

    
    getValue: function() {
        var me = this,
            val = me.rawToValue(me.processRawValue(me.getRawValue()));
        me.value = val;
        return val;
    },

    
    setValue: function(value) {
        var me = this;
        me.setRawValue(me.valueToRaw(value));
        return me.mixins.field.setValue.call(me, value);
    },

    onBoxReady: function() {
        var me = this;
        me.callParent();
        
        if (me.setReadOnlyOnBoxReady) {
            me.setReadOnly(me.readOnly);
        }
            
    },

    
    onDisable: function() {
        var me = this,
            inputEl = me.inputEl;
            
        me.callParent();
        if (inputEl) {
            inputEl.dom.disabled = true;
            if (me.hasActiveError()) {
                
                me.clearInvalid();
                me.needsValidateOnEnable = true;
            }
        }
    },

    
    onEnable: function() {
        var me = this,
            inputEl = me.inputEl;
            
        me.callParent();
        if (inputEl) {
            inputEl.dom.disabled = false;
            if (me.needsValidateOnEnable) {
                delete me.needsValidateOnEnable;
                
                me.forceValidation = true;
                me.isValid();
                delete me.forceValidation;
            }
        }
    },

    
    setReadOnly: function(readOnly) {
        var me = this,
            inputEl = me.inputEl;
        readOnly = !!readOnly;
        me[readOnly ? 'addCls' : 'removeCls'](me.readOnlyCls);
        me.readOnly = readOnly;
        if (inputEl) {
            inputEl.dom.readOnly = readOnly;
        } else if (me.rendering) {
            me.setReadOnlyOnBoxReady = true;
        }
        me.fireEvent('writeablechange', me, readOnly);
    },

    
    fireKey: function(e){
        if(e.isSpecialKey()){
            this.fireEvent('specialkey', this, new Ext.EventObjectImpl(e));
        }
    },

    
    initEvents : function(){
        var me = this,
            inputEl = me.inputEl,
            onChangeTask,
            onChangeEvent,
            events = me.checkChangeEvents,
            e,
            eLen   = events.length,
            event;

        if (inputEl) {
            me.mon(inputEl, Ext.EventManager.getKeyEvent(), me.fireKey,  me);

            
            onChangeTask = new Ext.util.DelayedTask(me.checkChange, me);
            me.onChangeEvent = onChangeEvent = function() {
                onChangeTask.delay(me.checkChangeBuffer);
            };

            for (e = 0; e < eLen; e++) {
                event = events[e];

                if (event === 'propertychange') {
                    me.usesPropertychange = true;
                }

                me.mon(inputEl, event, onChangeEvent);
            }
        }
        me.callParent();
    },

    doComponentLayout: function() {
        var me = this,
            inputEl = me.inputEl,
            usesPropertychange = me.usesPropertychange,
            ename = 'propertychange',
            onChangeEvent = me.onChangeEvent;

        
        
        
        if (usesPropertychange) {
            me.mun(inputEl, ename, onChangeEvent);
        }
        me.callParent(arguments);
        if (usesPropertychange) {
            me.mon(inputEl, ename, onChangeEvent);
        }
    },

    
    onDirtyChange: function(isDirty) {
        this[isDirty ? 'addCls' : 'removeCls'](this.dirtyCls);
    },


    
    isValid : function() {
        var me = this,
            disabled = me.disabled,
            validate = me.forceValidation || !disabled;
            
        
        return validate ? me.validateValue(me.processRawValue(me.getRawValue())) : disabled;
    },


    
    validateValue: function(value) {
        var me = this,
            errors = me.getErrors(value),
            isValid = Ext.isEmpty(errors);
        if (!me.preventMark) {
            if (isValid) {
                me.clearInvalid();
            } else {
                me.markInvalid(errors);
            }
        }

        return isValid;
    },

    
    markInvalid : function(errors) {
        
        var me = this,
            oldMsg = me.getActiveError(),
            active;
            
        me.setActiveErrors(Ext.Array.from(errors));
        active = me.getActiveError();
        if (oldMsg !== active) {
            me.setError(active);
        }
    },

    
    clearInvalid : function() {
        
        var me = this,
            hadError = me.hasActiveError();
            
        delete me.needsValidateOnEnable;
        me.unsetActiveError();
        if (hadError) {
            me.setError('');
        }
    },
    
    
    setError: function(active){
        var me = this,
            msgTarget = me.msgTarget,
            prop;
            
        if (me.rendered) {
            if (msgTarget == 'title' || msgTarget == 'qtip') {
                if (me.rendered) {
                    prop = msgTarget == 'qtip' ? 'data-errorqtip' : 'title';
                }
                me.getActionEl().dom.setAttribute(prop, active || '');
            } else {
                me.updateLayout();
            }
        }
    },

    
    renderActiveError: function() {
        var me = this,
            hasError = me.hasActiveError();
        if (me.inputEl) {
            
            me.inputEl[hasError ? 'addCls' : 'removeCls'](me.invalidCls + '-field');
        }
        me.mixins.labelable.renderActiveError.call(me);
    },


    getActionEl: function() {
        return this.inputEl || this.el;
    }

});


Ext.define('Ext.form.field.VTypes', (function(){
    
    var alpha = /^[a-zA-Z_]+$/,
        alphanum = /^[a-zA-Z0-9_]+$/,

        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        email = /^(")?(?:[^\."])(?:(?:[\.])?(?:[\w\-!#$%&'*+/=?^_`{|}~]))*\1@(\w[\-\w]*\.){1,5}([A-Za-z]){2,6}$/,
        url = /(((^https?)|(^ftp)):\/\/((([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*)|(localhost|LOCALHOST))\/?)/i;

    
    return {
        singleton: true,
        alternateClassName: 'Ext.form.VTypes',

        
        'email' : function(v){
            return email.test(v);
        },
        
        
        'emailText' : 'This field should be an e-mail address in the format "user@example.com"',
        
        
        'emailMask' : /[\w.\-@'"!#$%&'*+/=?^_`{|}~]/i,

        
        'url' : function(v){
            return url.test(v);
        },
        
        
        'urlText' : 'This field should be a URL in the format "http:/'+'/www.example.com"',
        

        
        'alpha' : function(v){
            return alpha.test(v);
        },
        
        
        'alphaText' : 'This field should only contain letters and _',
        
        
        'alphaMask' : /[a-z_]/i,

        
        'alphanum' : function(v){
            return alphanum.test(v);
        },
        
        
        'alphanumText' : 'This field should only contain letters, numbers and _',
        
        
        'alphanumMask' : /[a-z0-9_]/i
    };
}()));


Ext.define('Ext.layout.component.field.Text', {
    extend:  Ext.layout.component.field.Field ,
    alias: 'layout.textfield',
                                       

    type: 'textfield',
    
    
    
    setWidthInDom: true,
    
    canGrowWidth: true,

    beginLayoutCycle: function(ownerContext) {
        this.callParent(arguments);
        
        
        if (ownerContext.heightModel.shrinkWrap) {
            ownerContext.inputContext.el.setStyle('height', '');
        }
    },

    measureContentWidth: function (ownerContext) {
        var me = this,
            owner = me.owner,
            width = me.callParent(arguments),
            inputContext = ownerContext.inputContext,
            inputEl, value, calcWidth, max, min;

        if (owner.grow && me.canGrowWidth && !ownerContext.state.growHandled) {
            inputEl = owner.inputEl;

            
            value = Ext.util.Format.htmlEncode(inputEl.dom.value || (owner.hasFocus ? '' : owner.emptyText) || '');
            value += owner.growAppend;
            calcWidth = inputEl.getTextWidth(value) + inputContext.getFrameInfo().width;

            max = owner.growMax;
            min = Math.min(max, width);
            max = Math.max(owner.growMin, max, min);

            
            calcWidth = Ext.Number.constrain(calcWidth, owner.growMin, max);
            inputContext.setWidth(calcWidth);
            ownerContext.state.growHandled = true;
            
            
            inputContext.domBlock(me, 'width');
            width = NaN;
        }
        return width;
    },
    
    publishInnerHeight: function(ownerContext, height) {
        ownerContext.inputContext.setHeight(height - this.measureLabelErrorHeight(ownerContext));
    },

    beginLayoutFixed: function(ownerContext, width, suffix) {
        var me = this,
            ieInputWidthAdjustment = me.ieInputWidthAdjustment;

        if (ieInputWidthAdjustment) {
            me.adjustIEInputPadding(ownerContext);
            if(suffix === 'px') {
                width -= ieInputWidthAdjustment;
            }
        }

        me.callParent(arguments);
    },

    adjustIEInputPadding: function(ownerContext) {
        
        this.owner.bodyEl.setStyle('padding-right', this.ieInputWidthAdjustment + 'px');
    }
});


Ext.define('Ext.form.field.Text', {
    extend: Ext.form.field.Base ,
    alias: 'widget.textfield',
                                                                           
    alternateClassName: ['Ext.form.TextField', 'Ext.form.Text'],

    

    

    
    size: 20,

    

    
    growMin : 30,

    
    growMax : 800,

    
    
    growAppend: 'W',
    

    

    

    

    
    allowBlank : true,

    
    validateBlank: false,

    
    allowOnlyWhitespace: true,

    
    minLength : 0,

    
    maxLength : Number.MAX_VALUE,

    

    
    
    minLengthText : 'The minimum length for this field is {0}',
    

    
    
    maxLengthText : 'The maximum length for this field is {0}',
    

    

    
    
    blankText : 'This field is required',
    

    

    

    
    regexText : '',

    

    
    emptyCls : Ext.baseCSSPrefix + 'form-empty-field',

    
    requiredCls : Ext.baseCSSPrefix + 'form-required-field',

    

    componentLayout: 'textfield',

    
    valueContainsPlaceholder : false,

    ariaRole: 'textbox',

    initComponent: function () {
        var me = this;
        
        if (me.allowOnlyWhitespace === false) {
            me.allowBlank = false;
        }

        me.callParent();

        me.addEvents(
            
            'autosize',

            
            'keydown',
            
            'keyup',
            
            'keypress'
        );
        me.addStateEvents('change');
        me.setGrowSizePolicy();
    },

    
    setGrowSizePolicy: function(){
        if (this.grow) {
            this.shrinkWrap |= 1; 
        }    
    },

    
    initEvents : function(){
        var me = this,
            el = me.inputEl;

        me.callParent();
        if(me.selectOnFocus || me.emptyText){
            me.mon(el, 'mousedown', me.onMouseDown, me);
        }
        if(me.maskRe || (me.vtype && me.disableKeyFilter !== true && (me.maskRe = Ext.form.field.VTypes[me.vtype+'Mask']))){
            me.mon(el, 'keypress', me.filterKeys, me);
        }

        if (me.enableKeyEvents) {
            me.mon(el, {
                scope: me,
                keyup: me.onKeyUp,
                keydown: me.onKeyDown,
                keypress: me.onKeyPress
            });
        }
    },

    
    isEqual: function(value1, value2) {
        return this.isEqualAsString(value1, value2);
    },

    
    onChange: function(newVal, oldVal) {
        this.callParent(arguments);
        this.autoSize();
    },

    getSubTplData: function() {
        var me = this,
            value = me.getRawValue(),
            isEmpty = me.emptyText && value.length < 1,
            maxLength = me.maxLength,
            placeholder;
            
        
        
        
        if (me.enforceMaxLength) {
            if (maxLength === Number.MAX_VALUE) {
                maxLength = undefined;
            }
        } else {
            maxLength = undefined;
        }

        if (isEmpty) {
            if (Ext.supports.Placeholder) {
                placeholder = me.emptyText;
            } else {
                value = me.emptyText;
                me.valueContainsPlaceholder = true;
            }
        }

        return Ext.apply(me.callParent(), {
            maxLength   : maxLength,
            readOnly    : me.readOnly,
            placeholder : placeholder,
            value       : value,
            fieldCls    : me.fieldCls + ((isEmpty && (placeholder || value)) ? ' ' + me.emptyCls : '') + (me.allowBlank ? '' :  ' ' + me.requiredCls)
        });
    },

    afterRender: function(){
        this.autoSize();
        this.callParent();
    },

    onMouseDown: function(e){
        var me = this;
        if(!me.hasFocus){
            me.mon(me.inputEl, 'mouseup', Ext.emptyFn, me, { single: true, preventDefault: true });
        }
    },

    
    processRawValue: function(value) {
        var me = this,
            stripRe = me.stripCharsRe,
            newValue;

        if (stripRe) {
            newValue = value.replace(stripRe, '');
            if (newValue !== value) {
                me.setRawValue(newValue);
                value = newValue;
            }
        }
        return value;
    },

    
    onDisable: function(){
        this.callParent();
        if (Ext.isIE) {
            this.inputEl.dom.unselectable = 'on';
        }
    },

    
    onEnable: function(){
        this.callParent();
        if (Ext.isIE) {
            this.inputEl.dom.unselectable = '';
        }
    },

    onKeyDown: function(e) {
        this.fireEvent('keydown', this, e);
    },

    onKeyUp: function(e) {
        this.fireEvent('keyup', this, e);
    },

    onKeyPress: function(e) {
        this.fireEvent('keypress', this, e);
    },

    
    reset : function(){
        this.callParent();
        this.applyEmptyText();
    },

    applyEmptyText : function(){
        var me = this,
            emptyText = me.emptyText,
            isEmpty;

        if (me.rendered && emptyText) {
            isEmpty = me.getRawValue().length < 1 && !me.hasFocus;

            if (Ext.supports.Placeholder) {
                me.inputEl.dom.placeholder = emptyText;
            } else if (isEmpty) {
                me.setRawValue(emptyText);
                me.valueContainsPlaceholder = true;
            }

            
            
            if (isEmpty) {
                me.inputEl.addCls(me.emptyCls);
            }

            me.autoSize();
        }
    },
    
    afterFirstLayout: function() {
        this.callParent();
        if (Ext.isIE && this.disabled) {
            var el = this.inputEl;
            if (el) {
                el.dom.unselectable = 'on';
            }
        }
    },
    
    
    beforeFocus : function(){
        var me = this,
            inputEl = me.inputEl,
            emptyText = me.emptyText,
            isEmpty;

        me.callParent(arguments);
        if ((emptyText && !Ext.supports.Placeholder) && (inputEl.dom.value === me.emptyText && me.valueContainsPlaceholder)) {
            me.setRawValue('');
            isEmpty = true;
            inputEl.removeCls(me.emptyCls);
            me.valueContainsPlaceholder = false;
        } else if (Ext.supports.Placeholder) {
            inputEl.removeCls(me.emptyCls);
        }
        
        if (me.selectOnFocus || isEmpty) {
            
            if (Ext.isWebKit) {
                if (!me.inputFocusTask) {
                    me.inputFocusTask = new Ext.util.DelayedTask(me.focusInput, me);
                }
                me.inputFocusTask.delay(1);
            } else {
                me.focusInput();
            }
        }
    },
    
    focusInput: function(){
        var input = this.inputEl;
        if (input) {
            input = input.dom;
            if (input) {
                input.select();
            }
        }    
    },

    onFocus: function() {
        var me = this;
        me.callParent(arguments);
        if (me.emptyText) {
            me.autoSize();
        }
    },

    
    postBlur : function(){
        var task = this.inputFocusTask;
        
        this.callParent(arguments);
        this.applyEmptyText();
        
        
        if (task) {
            task.cancel();
        }
        
    },

    
    filterKeys : function(e){
        
        if (e.ctrlKey && !e.altKey) {
            return;
        }
        var key = e.getKey(),
            charCode = String.fromCharCode(e.getCharCode());

        if((Ext.isGecko || Ext.isOpera) && (e.isNavKeyPress() || key === e.BACKSPACE || (key === e.DELETE && e.button === -1))){
            return;
        }

        if((!Ext.isGecko && !Ext.isOpera) && e.isSpecialKey() && !charCode){
            return;
        }
        if(!this.maskRe.test(charCode)){
            e.stopEvent();
        }
    },

    getState: function() {
        return this.addPropertyToState(this.callParent(), 'value');
    },

    applyState: function(state) {
        this.callParent(arguments);
        if(state.hasOwnProperty('value')) {
            this.setValue(state.value);
        }
    },

    
    getRawValue: function() {
        var me = this,
            v = me.callParent();
        if (v === me.emptyText && me.valueContainsPlaceholder) {
            v = '';
        }
        return v;
    },

    
    setValue: function(value) {
        var me = this,
            inputEl = me.inputEl;

        if (inputEl && me.emptyText && !Ext.isEmpty(value)) {
            inputEl.removeCls(me.emptyCls);
            me.valueContainsPlaceholder = false;
        }

        me.callParent(arguments);

        me.applyEmptyText();
        return me;
    },

    
    getErrors: function(value) {
        var me = this,
            errors = me.callParent(arguments),
            validator = me.validator,
            vtype = me.vtype,
            vtypes = Ext.form.field.VTypes,
            regex = me.regex,
            format = Ext.String.format,
            msg, trimmed, isBlank;

        value = value || me.processRawValue(me.getRawValue());

        if (Ext.isFunction(validator)) {
            msg = validator.call(me, value);
            if (msg !== true) {
                errors.push(msg);
            }
        }
        
        trimmed = me.allowOnlyWhitespace ? value : Ext.String.trim(value);

        if (trimmed.length < 1 || (value === me.emptyText && me.valueContainsPlaceholder)) {
            if (!me.allowBlank) {
                errors.push(me.blankText);
            }
            
            if (!me.validateBlank) {
                return errors;
            }
            isBlank = true;
        }

        
        
        if (!isBlank && value.length < me.minLength) {
            errors.push(format(me.minLengthText, me.minLength));
        }

        if (value.length > me.maxLength) {
            errors.push(format(me.maxLengthText, me.maxLength));
        }

        if (vtype) {
            if (!vtypes[vtype](value, me)) {
                errors.push(me.vtypeText || vtypes[vtype +'Text']);
            }
        }

        if (regex && !regex.test(value)) {
            errors.push(me.regexText || me.invalidText);
        }

        return errors;
    },

    
    selectText : function(start, end){
        var me = this,
            v = me.getRawValue(),
            doFocus = true,
            el = me.inputEl.dom,
            undef,
            range;

        if (v.length > 0) {
            start = start === undef ? 0 : start;
            end = end === undef ? v.length : end;
            if (el.setSelectionRange) {
                el.setSelectionRange(start, end);
            }
            else if(el.createTextRange) {
                range = el.createTextRange();
                range.moveStart('character', start);
                range.moveEnd('character', end - v.length);
                range.select();
            }
            doFocus = Ext.isGecko || Ext.isOpera;
        }
        if (doFocus) {
            me.focus();
        }
    },

    
    autoSize: function() {
        var me = this;
        if (me.grow && me.rendered) {
            me.autoSizing = true;
            me.updateLayout();
        }
    },

    afterComponentLayout: function() {
        var me = this,
            width;

        me.callParent(arguments);
        if (me.autoSizing) {
            width = me.inputEl.getWidth();
            if (width !== me.lastInputWidth) {
                me.fireEvent('autosize', me, width);
                me.lastInputWidth = width;
                delete me.autoSizing;
            }
        }
    },
    
    onDestroy: function(){
        var me = this;
        me.callParent();
        
        if (me.inputFocusTask) {
            me.inputFocusTask.cancel();
            me.inputFocusTask = null;
        }
    }
});


Ext.define('Ext.layout.component.field.TextArea', {
    extend:  Ext.layout.component.field.Text ,
    alias: 'layout.textareafield',

    type: 'textareafield',
    
    canGrowWidth: false,
    
    naturalSizingProp: 'cols',
    
    beginLayout: function(ownerContext){
        this.callParent(arguments);
        ownerContext.target.inputEl.setStyle('height', '');
    },

    measureContentHeight: function (ownerContext) {
        var me = this,
            owner = me.owner,
            height = me.callParent(arguments),
            inputContext, inputEl, value, max, curWidth, calcHeight;

        if (owner.grow && !ownerContext.state.growHandled) {
            inputContext = ownerContext.inputContext;
            inputEl = owner.inputEl;
            curWidth = inputEl.getWidth(true); 

            
            value = Ext.util.Format.htmlEncode(inputEl.dom.value) || '&#160;';
            value += owner.growAppend;
            
            
            value = value.replace(/\n/g, '<br/>');

            
            calcHeight = Ext.util.TextMetrics.measure(inputEl, value, curWidth).height +
                         inputContext.getBorderInfo().height + inputContext.getPaddingInfo().height;

            
            calcHeight = Ext.Number.constrain(calcHeight, owner.growMin, owner.growMax);
            inputContext.setHeight(calcHeight);
            ownerContext.state.growHandled = true;
            
            
            inputContext.domBlock(me, 'height');
            height = NaN;
        }
        return height;
    }
});


Ext.define('Ext.form.field.TextArea', {
    extend: Ext.form.field.Text ,
    alias: ['widget.textareafield', 'widget.textarea'],
    alternateClassName: 'Ext.form.TextArea',
               
                         
                                              
                              
      

    
    
    
    
    
    
    
    
    
    
    fieldSubTpl: [
        '<textarea id="{id}" role="{role}" {inputAttrTpl}',
            '<tpl if="name"> name="{name}"</tpl>',
            '<tpl if="rows"> rows="{rows}" </tpl>',
            '<tpl if="cols"> cols="{cols}" </tpl>',
            '<tpl if="placeholder"> placeholder="{placeholder}"</tpl>',
            '<tpl if="size"> size="{size}"</tpl>',
            '<tpl if="maxLength !== undefined"> maxlength="{maxLength}"</tpl>',
            '<tpl if="readOnly"> readonly="readonly"</tpl>',
            '<tpl if="disabled"> disabled="disabled"</tpl>',
            '<tpl if="tabIdx"> tabIndex="{tabIdx}"</tpl>',
            ' class="{fieldCls} {typeCls} {inputCls}" ',
            '<tpl if="fieldStyle"> style="{fieldStyle}"</tpl>',
            ' autocomplete="off">\n',
            '<tpl if="value">{[Ext.util.Format.htmlEncode(values.value)]}</tpl>',
        '</textarea>',
        {
            disableFormats: true
        }
    ],

    
    growMin: 60,

    
    growMax: 1000,

    
    growAppend: '\n-',

    
    cols: 20,

    
    rows: 4,

    
    enterIsSpecial: false,

    
    preventScrollbars: false,

    
    componentLayout: 'textareafield',
    
    setGrowSizePolicy: Ext.emptyFn,
    
    returnRe: /\r/g,

    inputCls: Ext.baseCSSPrefix + 'form-textarea',

    
    getSubTplData: function() {
        var me = this,
            fieldStyle = me.getFieldStyle(),
            ret = me.callParent();

        if (me.grow) {
            if (me.preventScrollbars) {
                ret.fieldStyle = (fieldStyle||'') + ';overflow:hidden;height:' + me.growMin + 'px';
            }
        }

        Ext.applyIf(ret, {
            cols: me.cols,
            rows: me.rows
        });

        return ret;
    },

    afterRender: function () {
        var me = this;

        me.callParent(arguments);

        me.needsMaxCheck = me.enforceMaxLength && me.maxLength !== Number.MAX_VALUE && !Ext.supports.TextAreaMaxLength;
        if (me.needsMaxCheck) {
            me.inputEl.on('paste', me.onPaste, me);
        }
    },
    
    
    
    
    
    
    
    transformRawValue: function(value){
        return this.stripReturns(value);
    },
    
    transformOriginalValue: function(value){
        return this.stripReturns(value); 
    },
    
    getValue: function(){
        return this.stripReturns(this.callParent());    
    },
    
    valueToRaw: function(value){
        value = this.stripReturns(value);
        return this.callParent([value]);
    },
    
    stripReturns: function(value){
        if (value && typeof value === 'string') {
            value = value.replace(this.returnRe, '');
        }
        return value;
    },

    onPaste: function(e){
        var me = this;
        if (!me.pasteTask) {
            me.pasteTask = new Ext.util.DelayedTask(me.pasteCheck, me);
        }
        
        me.pasteTask.delay(1);
    },
    
    pasteCheck: function(){
        var me = this,
            value = me.getValue(),
            max = me.maxLength;
            
        if (value.length > max) {
            value = value.substr(0, max);
            me.setValue(value);
        }
    },

    
    fireKey: function(e) {
        var me = this,
            key = e.getKey(),
            value;
            
        if (e.isSpecialKey() && (me.enterIsSpecial || (key !== e.ENTER || e.hasModifier()))) {
            me.fireEvent('specialkey', me, e);
        }
        
        if (me.needsMaxCheck && key !== e.BACKSPACE && key !== e.DELETE && !e.isNavKeyPress() && !me.isCutCopyPasteSelectAll(e, key)) {
            value = me.getValue();
            if (value.length >= me.maxLength) {
                e.stopEvent();
            }
        }
    },
    
    isCutCopyPasteSelectAll: function(e, key) {
        if (e.ctrlKey) {
            return key === e.A || key === e.C || key === e.V || key === e.X;
        }
        return false;
    },

    
    autoSize: function() {
        var me = this,
            height;

        if (me.grow && me.rendered) {
            me.updateLayout();
            height = me.inputEl.getHeight();
            if (height !== me.lastInputHeight) {
                
                me.fireEvent('autosize', me, height);
                me.lastInputHeight = height;
            }
        }
    },

    beforeDestroy: function(){
        var task = this.pasteTask;
        if (task) {
            task.cancel();
            this.pasteTask = null;
        }    
        this.callParent();
    }
});


Ext.define('Ext.form.field.Display', {
    extend: Ext.form.field.Base ,
    alias: 'widget.displayfield',
                                                   
    alternateClassName: ['Ext.form.DisplayField', 'Ext.form.Display'],
    
    ariaRole: 'textbox',
    
    fieldSubTpl: [
        '<div id="{id}" role="{role}" {inputAttrTpl}',
        '<tpl if="fieldStyle"> style="{fieldStyle}"</tpl>', 
        ' class="{fieldCls}">{value}</div>',
        {
            compiled: true,
            disableFormats: true
        }
    ],

    
    readOnly: true,

    
    fieldCls: Ext.baseCSSPrefix + 'form-display-field',

    fieldBodyCls: Ext.baseCSSPrefix + 'form-display-field-body',

    
    htmlEncode: false,
    
    
    
    

    noWrap: false,
    
    
    validateOnChange: false,

    initEvents: Ext.emptyFn,

    submitValue: false,
    
    valueToRaw: function(value) {
        if (!value && value !== 0) {
            return '';
        } else {
            return value;
        }
    },
    
    isDirty: function(){
        return false;
    },

    isValid: function() {
        return true;
    },

    validate: function() {
        return true;
    },

    getRawValue: function() {
        return this.rawValue;
    },

    setRawValue: function(value) {
        var me = this;
            
        value = Ext.value(value, '');
        me.rawValue = value;
        if (me.rendered) {
            me.inputEl.dom.innerHTML = me.getDisplayValue();
            me.updateLayout();
        }
        return value;
    },

    
    getDisplayValue: function() {
        var me = this,
            value = this.getRawValue(),
            display;
        if (me.renderer) {
             display = me.renderer.call(me.scope || me, value, me);
        } else {
             display = me.htmlEncode ? Ext.util.Format.htmlEncode(value) : value;
        }
        return display;
    },
        
    getSubTplData: function() {
        var ret = this.callParent(arguments);

        ret.value = this.getDisplayValue();

        return ret;
    }

    
    
    
    
});


Ext.define('Ext.layout.container.Anchor', {

    

    alias: 'layout.anchor',
    extend:  Ext.layout.container.Auto ,
    alternateClassName: 'Ext.layout.AnchorLayout',

    

    type: 'anchor',

    

    
    defaultAnchor: '100%',

    parseAnchorRE: /^(r|right|b|bottom)$/i,

    manageOverflow: true,

    beginLayoutCycle: function (ownerContext) {
        var me = this,
            dimensions = 0,
            anchorSpec, childContext, childItems, i, length, target;

        me.callParent(arguments);

        childItems = ownerContext.childItems; 
        length = childItems.length;

        for (i = 0; i < length; ++i) {
            childContext = childItems[i];
            anchorSpec = childContext.target.anchorSpec;

            if (anchorSpec) {
                if (childContext.widthModel.calculated && anchorSpec.right) {
                    dimensions |= 1;
                }
                if (childContext.heightModel.calculated && anchorSpec.bottom) {
                    dimensions |= 2;
                }

                if (dimensions == 3) { 
                    break;
                }
            }
        }

        ownerContext.anchorDimensions = dimensions;

    },

    calculateItems: function (ownerContext, containerSize) {
        var me = this,
            childItems = ownerContext.childItems,
            length = childItems.length,
            gotHeight = containerSize.gotHeight,
            gotWidth = containerSize.gotWidth,
            ownerHeight = containerSize.height,
            ownerWidth = containerSize.width,
            knownDimensions = (gotWidth ? 1 : 0) | (gotHeight ? 2 : 0),
            anchorDimensions = ownerContext.anchorDimensions,
            anchorSpec, childContext, childMargins, height, i, width;

        if (!anchorDimensions) {
            return true;
        }

        for (i = 0; i < length; i++) {
            childContext = childItems[i];
            childMargins = childContext.getMarginInfo();
            anchorSpec = childContext.target.anchorSpec;

            
            
            
            
            if (gotWidth && childContext.widthModel.calculated) {
                width = anchorSpec.right(ownerWidth) - childMargins.width;
                width = me.adjustWidthAnchor(width, childContext);

                childContext.setWidth(width);
            }

            
            if (gotHeight && childContext.heightModel.calculated) {
                height = anchorSpec.bottom(ownerHeight) - childMargins.height;
                height = me.adjustHeightAnchor(height, childContext);

                childContext.setHeight(height);
            }
        }

        
        return (knownDimensions & anchorDimensions) === anchorDimensions;
    },


    
    anchorFactory: {
        offset: function (delta) {
            return function(v) {
                return v + delta;
            };
        },
        ratio: function (ratio) {
            return function(v) {
                return Math.floor(v * ratio);
            };
        },
        standard: function (diff) {
            return function(v) {
                return v - diff;
            };
        }
    },

    parseAnchor: function(a, start, cstart) {
        if (a && a != 'none') {
            var factory = this.anchorFactory,
                delta;

            if (this.parseAnchorRE.test(a)) {
                return factory.standard(cstart - start);
            }    
            if (a.indexOf('%') != -1) {
                return factory.ratio(parseFloat(a.replace('%', '')) * 0.01);
            }    
            delta = parseInt(a, 10);
            if (!isNaN(delta)) {
                return factory.offset(delta);
            }
        }
        return null;
    },

    
    adjustWidthAnchor: function(value, childContext) {
        return value;
    },

    
    adjustHeightAnchor: function(value, childContext) {
        return value;
    },

    configureItem: function(item) {
        var me = this,
            owner = me.owner,
            anchor= item.anchor,
            anchorsArray,
            anchorWidth,
            anchorHeight;

        me.callParent(arguments);

        if (!item.anchor && item.items && !Ext.isNumber(item.width) && !(Ext.isIE6 && Ext.isStrict)) {
            item.anchor = anchor = me.defaultAnchor;
        }

         
        if (owner.anchorSize) {
            if (typeof owner.anchorSize == 'number') {
                anchorWidth = owner.anchorSize;
            } else {
                anchorWidth = owner.anchorSize.width;
                anchorHeight = owner.anchorSize.height;
            }
        } else {
            anchorWidth = owner.initialConfig.width;
            anchorHeight = owner.initialConfig.height;
        }

        if (anchor) {
            
            anchorsArray = anchor.split(' ');
            item.anchorSpec = {
                right: me.parseAnchor(anchorsArray[0], item.initialConfig.width, anchorWidth),
                bottom: me.parseAnchor(anchorsArray[1], item.initialConfig.height, anchorHeight)
            };
        }
    },

    sizePolicy: {
        $: {
            readsWidth: 1,
            readsHeight: 1,
            setsWidth: 0,
            setsHeight: 0
        },
        b: {
            readsWidth: 1,
            readsHeight: 0,
            setsWidth: 0,
            setsHeight: 1
        },
        r: {
            $: {
                readsWidth: 0,
                readsHeight: 1,
                setsWidth: 1,
                setsHeight: 0
            },
            b: {
                readsWidth: 0,
                readsHeight: 0,
                setsWidth: 1,
                setsHeight: 1
            }
        }
    },

    getItemSizePolicy: function (item) {
        var anchorSpec = item.anchorSpec,
            key = '$',
            policy = this.sizePolicy,
            sizeModel;

        if (anchorSpec) {
            sizeModel = this.owner.getSizeModel();
            if (anchorSpec.right && !sizeModel.width.shrinkWrap) {
                policy = policy.r;
            }
            if (anchorSpec.bottom && !sizeModel.height.shrinkWrap) {
                key = 'b';
            }
        }

        return policy[key];
    }
});




Ext.define('Ext.window.MessageBox', {
    extend:  Ext.window.Window ,

               
                              
                              
                                  
                                 
                            
                                      
                                    
                         
      

    alias: 'widget.messagebox',

    
    OK : 1,
    
    YES : 2,
    
    NO : 4,
    
    CANCEL : 8,
    
    OKCANCEL : 9,
    
    YESNO : 6,
    
    YESNOCANCEL : 14,
    
    INFO : Ext.baseCSSPrefix + 'message-box-info',
    
    WARNING : Ext.baseCSSPrefix + 'message-box-warning',
    
    QUESTION : Ext.baseCSSPrefix + 'message-box-question',
    
    ERROR : Ext.baseCSSPrefix + 'message-box-error',

    
    hideMode: 'offsets',
    closeAction: 'hide',
    resizable: false,
    title: '&#160;',

    defaultMinWidth: 250,
    defaultMaxWidth: 600,
    defaultMinHeight: 110,
    defaultMaxHeight: 500,
    
    
    
    minWidth: null,
    maxWidth: null,
    minHeight: null,
    maxHeight: null,
    constrain: true,

    cls: [Ext.baseCSSPrefix + 'message-box', Ext.baseCSSPrefix + 'hide-offsets'],

    layout: {
        type: 'vbox',
        align: 'stretch'
    },

    
    shrinkWrapDock: true,

    
    defaultTextHeight : 75,
    
    minProgressWidth : 250,
    
    minPromptWidth: 250,
    
    
    buttonText: {
        ok: 'OK',
        yes: 'Yes',
        no: 'No',
        cancel: 'Cancel'
    },
    

    buttonIds: [
        'ok', 'yes', 'no', 'cancel'
    ],

    
    titleText: {
        confirm: 'Confirm',
        prompt: 'Prompt',
        wait: 'Loading...',
        alert: 'Attention'
    },
    

    iconHeight: 35,
    iconWidth: 50,
    
    ariaRole: 'alertdialog',

    makeButton: function(btnIdx) {
        var btnId = this.buttonIds[btnIdx];
        return new Ext.button.Button({
            handler: this.btnCallback,
            itemId: btnId,
            scope: this,
            text: this.buttonText[btnId],
            minWidth: 75
        });
    },

    btnCallback: function(btn) {
        var me = this,
            value,
            field;

        if (me.cfg.prompt || me.cfg.multiline) {
            if (me.cfg.multiline) {
                field = me.textArea;
            } else {
                field = me.textField;
            }
            value = field.getValue();
            field.reset();
        }

        
        me.hide();
        me.userCallback(btn.itemId, value, me.cfg);
    },

    hide: function() {
        var me = this,
            cls = me.cfg.cls;

        me.progressBar.reset();
        if (cls) {
            me.removeCls(cls);
        }
        me.callParent(arguments);
    },

    constructor: function(cfg) {
        var me = this;

        me.callParent(arguments);

        
        
        me.minWidth = me.defaultMinWidth = (me.minWidth || me.defaultMinWidth);
        me.maxWidth = me.defaultMaxWidth = (me.maxWidth || me.defaultMaxWidth);
        me.minHeight = me.defaultMinHeight = (me.minHeight || me.defaultMinHeight);
        me.maxHeight = me.defaultMaxHeight = (me.maxHeight || me.defaultMaxHeight);
    },

    initComponent: function(cfg) {
        var me = this,
            baseId = me.id,
            i, button;

        me.title = '&#160;';

        me.topContainer = new Ext.container.Container({
            layout: 'hbox',
            padding: 10,
            style: {
                overflow: 'hidden'
            },
            items: [
                me.iconComponent = new Ext.Component({
                    width: me.iconWidth,
                    height: me.iconHeight
                }),
                me.promptContainer = new Ext.container.Container({
                    flex: 1,
                    layout: 'anchor',
                    items: [
                        me.msg = new Ext.form.field.Display({
                            id: baseId + '-displayfield',
                            cls: me.baseCls + '-text'
                        }),
                        me.textField = new Ext.form.field.Text({
                            id: baseId + '-textfield',
                            anchor: '100%',
                            enableKeyEvents: true,
                            listeners: {
                                keydown: me.onPromptKey,
                                scope: me
                            }
                        }),
                        me.textArea = new Ext.form.field.TextArea({
                            id: baseId + '-textarea',
                            anchor: '100%',
                            height: 75
                        })
                    ]
                })
            ]
        });
        me.progressBar = new Ext.ProgressBar({
            id: baseId + '-progressbar',
            margin: '0 10 10 10'
        });

        me.items = [me.topContainer, me.progressBar];

        
        me.msgButtons = [];
        for (i = 0; i < 4; i++) {
            button = me.makeButton(i);
            me.msgButtons[button.itemId] = button;
            me.msgButtons.push(button);
        }
        me.bottomTb = new Ext.toolbar.Toolbar({
            id: baseId + '-toolbar',
            ui: 'footer',
            dock: 'bottom',
            layout: {
                pack: 'center'
            },
            items: [
                me.msgButtons[0],
                me.msgButtons[1],
                me.msgButtons[2],
                me.msgButtons[3]
            ]
        });
        me.dockedItems = [me.bottomTb];
        me.on('close', me.onClose, me);
        me.callParent();
    },

    onClose: function(){
        var btn = this.header.child('[type=close]');
        
        btn.itemId = 'cancel';
        this.btnCallback(btn);
        delete btn.itemId;
    },

    onPromptKey: function(textField, e) {
        var me = this;

        if (e.keyCode === e.RETURN || e.keyCode === 10) {
            if (me.msgButtons.ok.isVisible()) {
                me.msgButtons.ok.handler.call(me, me.msgButtons.ok);
            } else if (me.msgButtons.yes.isVisible()) {
                me.msgButtons.yes.handler.call(me, me.msgButtons.yes);
            }
        }
    },

    reconfigure: function(cfg) {
        var me = this,
            buttons = 0,
            hideToolbar = true,
            oldButtonText = me.buttonText,
            resizer = me.resizer,
            resizeTracker, width, height, i, textArea, textField,
            msg, progressBar, msgButtons;

        
        me.updateButtonText();

        cfg = cfg || {};
        me.cfg = cfg;
        if (cfg.width) {
            width = cfg.width;
        }

        if (cfg.height) {
            height = cfg.height;
        }

        me.minWidth = cfg.minWidth || me.defaultMinWidth;
        me.maxWidth = cfg.maxWidth || me.defaultMaxWidth;
        me.minHeight = cfg.minHeight || me.defaultMinHeight;
        me.maxHeight = cfg.maxHeight || me.defaultMaxHeight;

        if (resizer) {
            resizeTracker = resizer.resizeTracker;
            resizer.minWidth = resizeTracker.minWidth = me.minWidth;
            resizer.maxWidth = resizeTracker.maxWidth = me.maxWidth;
            resizer.minHeight = resizeTracker.minHeight = me.minHeight;
            resizer.maxHeight = resizeTracker.maxHeight = me.maxHeight;
        }

        
        delete me.defaultFocus;
        if (cfg.defaultFocus) {
            me.defaultFocus = cfg.defaultFocus;
        }

        
        me.animateTarget = cfg.animateTarget || undefined;

        
        me.modal = cfg.modal !== false;

        
        me.setTitle(cfg.title || '');
        me.setIconCls(cfg.iconCls || '');

        
        if (Ext.isObject(cfg.buttons)) {
            me.buttonText = cfg.buttons;
            buttons = 0;
        } else {
            me.buttonText = cfg.buttonText || me.buttonText;
            buttons = Ext.isNumber(cfg.buttons) ? cfg.buttons : 0;
        }

        
        
        buttons = buttons | me.updateButtonText();

        
        me.buttonText = oldButtonText;

        
        
        Ext.suspendLayouts();
        delete me.width;
        delete me.height;
        if (width || height) {
            if (width) {
                me.setWidth(width);
            }

            if (height) {
                me.setHeight(height);
            }
        }
        me.hidden = false;
        if (!me.rendered) {
            me.render(Ext.getBody());
        }

        
        me.closable = cfg.closable !== false && !cfg.wait;
        me.header.child('[type=close]').setVisible(me.closable);

        
        if (!cfg.title && !me.closable && !cfg.iconCls) {
            me.header.hide();
        } else {
            me.header.show();
        }

        
        me.liveDrag = !cfg.proxyDrag;

        
        me.userCallback = Ext.Function.bind(cfg.callback ||cfg.fn || Ext.emptyFn, cfg.scope || Ext.global);

        
        me.setIcon(cfg.icon, cfg.iconWidth, cfg.iconHeight);

        
        msg = me.msg;
        if (cfg.msg) {
            msg.setValue(cfg.msg);
            msg.show();
        } else {
            msg.hide();
        }

        
        textArea = me.textArea;
        textField = me.textField;
        if (cfg.prompt || cfg.multiline) {
            me.multiline = cfg.multiline;
            if (cfg.multiline) {
                textArea.setValue(cfg.value);
                textArea.setHeight(cfg.defaultTextHeight || me.defaultTextHeight);
                textArea.show();
                textField.hide();
                me.defaultFocus = textArea;
            } else {
                textField.setValue(cfg.value);
                textArea.hide();
                textField.show();
                me.defaultFocus = textField;
            }
        } else {
            textArea.hide();
            textField.hide();
        }

        
        progressBar = me.progressBar;
        if (cfg.progress || cfg.wait) {
            progressBar.show();
            me.updateProgress(0, cfg.progressText);
            if(cfg.wait === true){
                progressBar.wait(cfg.waitConfig);
            }
        } else {
            progressBar.hide();
        }

        
        msgButtons = me.msgButtons;
        for (i = 0; i < 4; i++) {
            if (buttons & Math.pow(2, i)) {

                
                if (!me.defaultFocus) {
                    me.defaultFocus = msgButtons[i];
                }
                msgButtons[i].show();
                hideToolbar = false;
            } else {
                msgButtons[i].hide();
            }
        }

        
        if (hideToolbar) {
            me.bottomTb.hide();
        } else {
            me.bottomTb.show();
        }
        Ext.resumeLayouts(true);
    },

    
    updateButtonText: function() {
        var me = this,
            buttonText = me.buttonText,
            buttons = 0,
            btnId,
            btn;

        for (btnId in buttonText) {
            if (buttonText.hasOwnProperty(btnId)) {
                btn = me.msgButtons[btnId];
                if (btn) {
                    if (me.cfg && me.cfg.buttonText) {
                        buttons = buttons | Math.pow(2, Ext.Array.indexOf(me.buttonIds, btnId));
                    }
                    if (btn.text != buttonText[btnId]) {
                        btn.setText(buttonText[btnId]);
                    }
                }
            }
        }
        return buttons;
    },

    
    show: function(cfg) {
        var me = this,
            visibleFocusables;

        
        if (Ext.AbstractComponent.layoutSuspendCount) {
            Ext.on({
                resumelayouts: function() {
                    me.show(cfg);
                },
                single: true
            });
            return me;
        }

        me.reconfigure(cfg);
        if (cfg.cls) {
            me.addCls(cfg.cls);
        }

        
        
        visibleFocusables = me.query('textfield:not([hidden]),textarea:not([hidden]),button:not([hidden])');
        me.preventFocusOnActivate = !visibleFocusables.length;

        
        
        me.hidden = true;
        me.callParent();
        return me;
    },

    onShow: function() {
        this.callParent(arguments);
        this.center();
    },

    updateText: function(text) {
        this.msg.setValue(text);
    },

    
    setIcon : function(icon, width, height) {
        var me = this,
            iconCmp = me.iconComponent,
            cls = me.messageIconCls;

        if (cls) {
            iconCmp.removeCls(cls);
        }

        if (icon) {
            iconCmp.show();
            iconCmp.setSize(width || me.iconWidth, height || me.iconHeight);
            iconCmp.addCls(Ext.baseCSSPrefix + 'dlg-icon');
            iconCmp.addCls(me.messageIconCls = icon);
        } else {
            iconCmp.removeCls(Ext.baseCSSPrefix + 'dlg-icon');
            iconCmp.hide();
        }
        return me;
    },

    
    updateProgress : function(value, progressText, msg){
        this.progressBar.updateProgress(value, progressText);
        if (msg){
            this.updateText(msg);
        }
        return this;
    },

    onEsc: function() {
        if (this.closable !== false) {
            this.callParent(arguments);
        }
    },

    
    confirm: function(cfg, msg, fn, scope) {
        if (Ext.isString(cfg)) {
            cfg = {
                title: cfg,
                icon: this.QUESTION,
                msg: msg,
                buttons: this.YESNO,
                callback: fn,
                scope: scope
            };
        }
        return this.show(cfg);
    },

    
    prompt : function(cfg, msg, fn, scope, multiline, value){
        if (Ext.isString(cfg)) {
            cfg = {
                prompt: true,
                title: cfg,
                minWidth: this.minPromptWidth,
                msg: msg,
                buttons: this.OKCANCEL,
                callback: fn,
                scope: scope,
                multiline: multiline,
                value: value
            };
        }
        return this.show(cfg);
    },

    
    wait : function(cfg, title, config){
        if (Ext.isString(cfg)) {
            cfg = {
                title : title,
                msg : cfg,
                closable: false,
                wait: true,
                modal: true,
                minWidth: this.minProgressWidth,
                waitConfig: config
            };
        }
        return this.show(cfg);
    },

    
    alert: function(cfg, msg, fn, scope) {
        if (Ext.isString(cfg)) {
            cfg = {
                title : cfg,
                msg : msg,
                buttons: this.OK,
                fn: fn,
                scope : scope,
                minWidth: this.minWidth
            };
        }
        return this.show(cfg);
    },

    
    progress : function(cfg, msg, progressText){
        if (Ext.isString(cfg)) {
            cfg = {
                title: cfg,
                msg: msg,
                progress: true,
                progressText: progressText
            };
        }
        return this.show(cfg);
    }
}, function() {
    
    Ext.MessageBox = Ext.Msg = new this();
});


Ext.define('Ext.form.Basic', {
    extend:  Ext.util.Observable ,
    alternateClassName: 'Ext.form.BasicForm',
                                                                                            
                                                                                   

    
    constructor: function(owner, config) {
        var me = this,
            reader;

        
        me.owner = owner;
        
        me.checkValidityTask = new Ext.util.DelayedTask(me.checkValidity, me);
        me.checkDirtyTask = new Ext.util.DelayedTask(me.checkDirty, me);
        
        
        
        
        me.monitor = new Ext.container.Monitor({
            selector: '[isFormField]:not([excludeForm])',
            scope: me,
            addHandler: me.onFieldAdd,
            removeHandler: me.onFieldRemove
        });
        me.monitor.bind(owner);

        Ext.apply(me, config);

        
        if (Ext.isString(me.paramOrder)) {
            me.paramOrder = me.paramOrder.split(/[\s,|]/);
        }
        
        reader = me.reader;
        if (reader && !reader.isReader) {
            if (typeof reader === 'string') {
                reader = {
                    type: reader
                };
            }
            me.reader = Ext.createByAlias('reader.' + reader.type, reader);
        }
        
        reader = me.errorReader;
        if (reader && !reader.isReader) {
            if (typeof reader === 'string') {
                reader = {
                    type: reader
                };
            }
            me.errorReader = Ext.createByAlias('reader.' + reader.type, reader);
        }

        me.addEvents(
            
            'beforeaction',
            
            'actionfailed',
            
            'actioncomplete',
            
            'validitychange',
            
            'dirtychange'
        );
        me.callParent();
    },

    
    initialize : function() {
        this.initialized = true;
        this.onValidityChange(!this.hasInvalidField());
    },


    

    

    

    

    

    
    timeout: 30,

    

    

    
    paramsAsHash: false,

    
    
    waitTitle: 'Please Wait...',
    

    
    trackResetOnLoad: false,

    

    

    


    
    wasDirty: false,


    
    destroy: function() {
        var me = this,
            mon = me.monitor;
        
        if (mon) {
            mon.unbind();
            me.monitor = null;
        }
        me.clearListeners();
        me.checkValidityTask.cancel();
        me.checkDirtyTask.cancel();
        me.isDestroyed = true;
    },
    
    onFieldAdd: function(field){
        var me = this;
        
        me.mon(field, 'validitychange', me.checkValidityDelay, me);
        me.mon(field, 'dirtychange', me.checkDirtyDelay, me);
        if (me.initialized) {
            me.checkValidityDelay();
        }
    },
    
    onFieldRemove: function(field){
        var me = this;
        
        me.mun(field, 'validitychange', me.checkValidityDelay, me);
        me.mun(field, 'dirtychange', me.checkDirtyDelay, me);
        if (me.initialized) {
            me.checkValidityDelay();
        }
    },
    
    
    getFields: function() {
        return this.monitor.getItems();
    },

    
    getBoundItems: function() {
        var boundItems = this._boundItems;
        
        if (!boundItems || boundItems.getCount() === 0) {
            boundItems = this._boundItems = new Ext.util.MixedCollection();
            boundItems.addAll(this.owner.query('[formBind]'));
        }
        
        return boundItems;
    },

    
    hasInvalidField: function() {
        return !!this.getFields().findBy(function(field) {
            var preventMark = field.preventMark,
                isValid;
            field.preventMark = true;
            isValid = field.isValid();
            field.preventMark = preventMark;
            return !isValid;
        });
    },

    
    isValid: function() {
        var me = this,
            invalid;
        Ext.suspendLayouts();
        invalid = me.getFields().filterBy(function(field) {
            return !field.validate();
        });
        Ext.resumeLayouts(true);
        return invalid.length < 1;
    },

    
    checkValidity: function() {
        var me = this,
            valid;
        
        if (me.isDestroyed) {
            return;
        }
            
        valid = !me.hasInvalidField();
        if (valid !== me.wasValid) {
            me.onValidityChange(valid);
            me.fireEvent('validitychange', me, valid);
            me.wasValid = valid;
        }
    },
    
    checkValidityDelay: function(){
        this.checkValidityTask.delay(10);
    },

    
    onValidityChange: function(valid) {
        var boundItems = this.getBoundItems(),
            items, i, iLen, cmp;

        if (boundItems) {
            items = boundItems.items;
            iLen  = items.length;

            for (i = 0; i < iLen; i++) {
                cmp = items[i];

                if (cmp.disabled === valid) {
                    cmp.setDisabled(!valid);
                }
            }
        }
    },

    
    isDirty: function() {
        return !!this.getFields().findBy(function(f) {
            return f.isDirty();
        });
    },
    
    checkDirtyDelay: function(){
        this.checkDirtyTask.delay(10);
    },

    
    checkDirty: function() {
        var me = this,
            dirty;
            
        if (me.isDestroyed) {
            return;
        }
            
        dirty = this.isDirty();
        if (dirty !== this.wasDirty) {
            this.fireEvent('dirtychange', this, dirty);
            this.wasDirty = dirty;
        }
    },

    
    hasUpload: function() {
        return !!this.getFields().findBy(function(f) {
            return f.isFileUpload();
        });
    },

    
    doAction: function(action, options) {
        if (Ext.isString(action)) {
            action = Ext.ClassManager.instantiateByAlias('formaction.' + action, Ext.apply({}, options, {form: this}));
        }
        if (this.fireEvent('beforeaction', this, action) !== false) {
            this.beforeAction(action);
            Ext.defer(action.run, 100, action);
        }
        return this;
    },

    
    submit: function(options) {
        options = options || {};
        var me = this,
            action;
            
        if (options.standardSubmit || me.standardSubmit) {
            action = 'standardsubmit';
        } else {
            action = me.api ? 'directsubmit' : 'submit';
        }
            
        return me.doAction(action, options);
    },

    
    load: function(options) {
        return this.doAction(this.api ? 'directload' : 'load', options);
    },

    
    updateRecord: function(record) {
        record = record || this._record;
        if (!record) {
            return this;
        }
        
        var fields = record.fields.items,
            values = this.getFieldValues(),
            obj = {},
            i = 0,
            len = fields.length,
            name;

        for (; i < len; ++i) {
            name  = fields[i].name;

            if (values.hasOwnProperty(name)) {
                obj[name] = values[name];
            }
        }

        record.beginEdit();
        record.set(obj);
        record.endEdit();

        return this;
    },

    
    loadRecord: function(record) {
        this._record = record;
        return this.setValues(record.getData());
    },

    
    getRecord: function() {
        return this._record;
    },

    
    beforeAction: function(action) {
        var me = this,
            waitMsg = action.waitMsg,
            maskCls = Ext.baseCSSPrefix + 'mask-loading',
            fields  = me.getFields().items,
            f,
            fLen    = fields.length,
            field, waitMsgTarget;

        
        for (f = 0; f < fLen; f++) {
            field = fields[f];

            if (field.isFormField && field.syncValue) {
                field.syncValue();
            }
        }

        if (waitMsg) {
            waitMsgTarget = me.waitMsgTarget;
            if (waitMsgTarget === true) {
                me.owner.el.mask(waitMsg, maskCls);
            } else if (waitMsgTarget) {
                waitMsgTarget = me.waitMsgTarget = Ext.get(waitMsgTarget);
                waitMsgTarget.mask(waitMsg, maskCls);
            } else {
                me.floatingAncestor = me.owner.up('[floating]');

                
                
                
                
                
                
                
                if (me.floatingAncestor) {
                    me.savePreventFocusOnActivate = me.floatingAncestor.preventFocusOnActivate;
                    me.floatingAncestor.preventFocusOnActivate = true;
                }
                Ext.MessageBox.wait(waitMsg, action.waitTitle || me.waitTitle);
            }
        }
    },

    
    afterAction: function(action, success) {
        var me = this;
        if (action.waitMsg) {
            var messageBox = Ext.MessageBox,
                waitMsgTarget = me.waitMsgTarget;
            if (waitMsgTarget === true) {
                me.owner.el.unmask();
            } else if (waitMsgTarget) {
                waitMsgTarget.unmask();
            } else {
                messageBox.hide();
            }
        }
        
        if (me.floatingAncestor) {
            me.floatingAncestor.preventFocusOnActivate = me.savePreventFocusOnActivate;
        }
        if (success) {
            if (action.reset) {
                me.reset();
            }
            Ext.callback(action.success, action.scope || action, [me, action]);
            me.fireEvent('actioncomplete', me, action);
        } else {
            Ext.callback(action.failure, action.scope || action, [me, action]);
            me.fireEvent('actionfailed', me, action);
        }
    },


    
    findField: function(id) {
        return this.getFields().findBy(function(f) {
            return f.id === id || f.getName() === id;
        });
    },


    
    markInvalid: function(errors) {
        var me = this,
            e, eLen, error, value,
            key;

        function mark(fieldId, msg) {
            var field = me.findField(fieldId);
            if (field) {
                field.markInvalid(msg);
            }
        }

        if (Ext.isArray(errors)) {
            eLen = errors.length;

            for (e = 0; e < eLen; e++) {
                error = errors[e];
                mark(error.id, error.msg);
            }
        } else if (errors instanceof Ext.data.Errors) {
            eLen  = errors.items.length;
            for (e = 0; e < eLen; e++) {
                error = errors.items[e];

                mark(error.field, error.message);
            }
        } else {
            for (key in errors) {
                if (errors.hasOwnProperty(key)) {
                    value = errors[key];
                    mark(key, value, errors);
                }
            }
        }
        return this;
    },

    
    setValues: function(values) {
        var me = this,
            v, vLen, val, field;

        function setVal(fieldId, val) {
            var field = me.findField(fieldId);
            if (field) {
                field.setValue(val);
                if (me.trackResetOnLoad) {
                    field.resetOriginalValue();
                }
            }
        }

        
        
        Ext.suspendLayouts();
        if (Ext.isArray(values)) {
            
            vLen = values.length;

            for (v = 0; v < vLen; v++) {
                val = values[v];

                setVal(val.id, val.value);
            }
        } else {
            
            Ext.iterate(values, setVal);
        }
        Ext.resumeLayouts(true);
        return this;
    },

    
    getValues: function(asString, dirtyOnly, includeEmptyText, useDataValues, isSubmitting) {
        var values  = {},
            fields  = this.getFields().items,
            fLen    = fields.length,
            isArray = Ext.isArray,
            field, data, val, bucket, name, f;

        for (f = 0; f < fLen; f++) {
            field = fields[f];
            if (!dirtyOnly || field.isDirty()) {
                data = field[useDataValues ? 'getModelData' : 'getSubmitData'](includeEmptyText, isSubmitting);

                if (Ext.isObject(data)) {
                    for (name in data) {
                        if (data.hasOwnProperty(name)) {
                            val = data[name];

                            if (includeEmptyText && val === '') {
                                val = field.emptyText || '';
                            }

                            if (!field.isRadio) {
                                if (values.hasOwnProperty(name)) {
                                    bucket = values[name];

                                    if (!isArray(bucket)) {
                                        bucket = values[name] = [bucket];
                                    }

                                    if (isArray(val)) {
                                        values[name] = bucket.concat(val);
                                    } else {
                                        bucket.push(val);
                                    }
                                } else {
                                    values[name] = val;
                                }
                            } else {
                                values[name] = values[name] || val;
                            }
                        }
                    }
                }
            }
        }

        if (asString) {
            values = Ext.Object.toQueryString(values);
        }
        return values;
    },

    
    getFieldValues: function(dirtyOnly) {
        return this.getValues(false, dirtyOnly, false, true);
    },

    
    clearInvalid: function() {
        Ext.suspendLayouts();

        var me     = this,
            fields = me.getFields().items,
            f,
            fLen   = fields.length;

        for (f = 0; f < fLen; f++) {
            fields[f].clearInvalid();
        }

        Ext.resumeLayouts(true);
        return me;
    },

    
    reset: function(resetRecord) {
        Ext.suspendLayouts();

        var me     = this,
            fields = me.getFields().items,
            f,
            fLen   = fields.length;

        for (f = 0; f < fLen; f++) {
            fields[f].reset();
        }

        Ext.resumeLayouts(true);
        
        if (resetRecord === true) {
            delete me._record;
        }
        return me;
    },

    
    applyToFields: function(obj) {
        var fields = this.getFields().items,
            f,
            fLen   = fields.length;

        for (f = 0; f < fLen; f++) {
            Ext.apply(fields[f], obj);
        }

        return this;
    },

    
    applyIfToFields: function(obj) {
        var fields = this.getFields().items,
            f,
            fLen   = fields.length;

        for (f = 0; f < fLen; f++) {
            Ext.applyIf(fields[f], obj);
        }

        return this;
    }
});


Ext.define('Ext.form.FieldAncestor', {
    
               
                               
      

    


    xhooks: {
        initHierarchyState: function(hierarchyState) {
            if (this.fieldDefaults) {
                if (hierarchyState.fieldDefaults) {
                    hierarchyState.fieldDefaults = Ext.apply(Ext.Object.chain(hierarchyState.fieldDefaults), this.fieldDefaults);
                } else {
                    hierarchyState.fieldDefaults = this.fieldDefaults;
                }
            }
        }
    },

    
    initFieldAncestor: function() {
        var me = this;

        me.addEvents(
            
            'fieldvaliditychange',

            
            'fielderrorchange'
        );

        
        
        
        me.monitor = new Ext.container.Monitor({
            scope: me,
            selector: '[isFormField]:not([excludeForm])',
            addHandler: me.onChildFieldAdd,
            removeHandler: me.onChildFieldRemove
        });
        me.initFieldDefaults();
    },
    
    initMonitor: function() {
        this.monitor.bind(this);    
    },
    
    onChildFieldAdd: function(field) {
        var me = this;
        me.mon(field, 'errorchange', me.handleFieldErrorChange, me);
        me.mon(field, 'validitychange', me.handleFieldValidityChange, me);
    },
    
    onChildFieldRemove: function(field) {
        var me = this;
        me.mun(field, 'errorchange', me.handleFieldErrorChange, me);
        me.mun(field, 'validitychange', me.handleFieldValidityChange, me);
    },

    
    initFieldDefaults: function() {
        if (!this.fieldDefaults) {
            this.fieldDefaults = {};
        }
    },

    
    handleFieldValidityChange: function(field, isValid) {
        var me = this;
        if (field !== me) {
            me.fireEvent('fieldvaliditychange', me, field, isValid);
            me.onFieldValidityChange(field, isValid);
        }
    },

    
    handleFieldErrorChange: function(labelable, activeError) {
        var me = this;
        if (labelable !== me) {
            me.fireEvent('fielderrorchange', me, labelable, activeError);
            me.onFieldErrorChange(labelable, activeError);
        }
    },

    
    onFieldValidityChange: Ext.emptyFn,

    
    onFieldErrorChange: Ext.emptyFn,
    
    beforeDestroy: function(){
        this.monitor.unbind();
        this.callParent();
    }

});


Ext.define('Ext.layout.component.field.FieldContainer', {

    

    extend:  Ext.layout.component.field.Field ,

    alias: 'layout.fieldcontainer',

    

    type: 'fieldcontainer',

    waitForOuterHeightInDom: true,
    waitForOuterWidthInDom: true,

    beginLayout: function(ownerContext) {
        var owner = this.owner;
        this.callParent(arguments);

        
        ownerContext.hasRawContent = true;
        owner.bodyEl.setStyle('height', '');
        owner.containerEl.setStyle('height', '');
        ownerContext.containerElContext = ownerContext.getEl('containerEl');
    },

    measureContentHeight: function (ownerContext) {
        
        
        
        return ownerContext.hasDomProp('containerLayoutDone') ? this.callParent(arguments) : NaN;
    },

    measureContentWidth: function (ownerContext) {
        
        return ownerContext.hasDomProp('containerLayoutDone') ? this.callParent(arguments) : NaN;
    },

    publishInnerWidth: function (ownerContext, width) {
        var bodyContext = ownerContext.bodyCellContext,
            innerWidth = bodyContext.el.getWidth();

        bodyContext.setWidth(innerWidth, false);
        ownerContext.containerElContext.setWidth(innerWidth, false);
    },
    
    publishInnerHeight: function (ownerContext, height) {
        var bodyContext = ownerContext.bodyCellContext,
            containerElContext = ownerContext.containerElContext;
            
        height -= this.measureLabelErrorHeight(ownerContext);
        bodyContext.setHeight(height);
        containerElContext.setHeight(height);
    }
});


Ext.define('Ext.form.FieldContainer', {
    extend:  Ext.container.Container ,
    mixins: {
        labelable:  Ext.form.Labelable ,
        fieldAncestor:  Ext.form.FieldAncestor 
    },
                                                          

    alias: 'widget.fieldcontainer',

    componentLayout: 'fieldcontainer',

    componentCls: Ext.baseCSSPrefix + 'form-fieldcontainer',
    
    
    
    customOverflowEl: 'containerEl',
    
    childEls: [
        'containerEl'
    ],

    

    
    combineLabels: false,

    
    
    labelConnector: ', ',
    

    
    combineErrors: false,

    maskOnDisable: false,
    
    
    invalidCls: '',

    fieldSubTpl: [
        '<div id="{id}-containerEl" class="{containerElCls}" role="presentation">',
            '{%this.renderContainer(out,values)%}',
        '</div>'
    ],

    initComponent: function() {
        var me = this;

        
        me.initLabelable();
        me.initFieldAncestor();
        
        me.callParent();
        me.initMonitor();
    },
    
    getOverflowEl: function(){
        return this.containerEl;    
    },

    
    onAdd: function(item) {
        var me = this;
        
        
        
        
        if (item.isLabelable && Ext.isGecko && me.layout.type === 'absolute' && !me.hideLabel && me.labelAlign !== 'top') {
            item.x += (me.labelWidth + me.labelPad);
        }
        me.callParent(arguments);
        if (item.isLabelable && me.combineLabels) {
            item.oldHideLabel = item.hideLabel;
            item.hideLabel = true;
        }
        me.updateLabel();
    },

    
    onRemove: function(item, isDestroying) {
        var me = this;
        me.callParent(arguments);
        if (!isDestroying) {
            if (item.isLabelable && me.combineLabels) {
                item.hideLabel = item.oldHideLabel;
            }
            me.updateLabel();
        }   
    },

    initRenderTpl: function() {
        var me = this;
        if (!me.hasOwnProperty('renderTpl')) {
            me.renderTpl = me.getTpl('labelableRenderTpl');
        }
        return me.callParent();
    },

    initRenderData: function() {
        var me = this,
            data = me.callParent();

        data.containerElCls = me.containerElCls;
        return Ext.applyIf(data, me.getLabelableRenderData());
    },

    
    getFieldLabel: function() {
        var label = this.fieldLabel || '';
        if (!label && this.combineLabels) {
            label = Ext.Array.map(this.query('[isFieldLabelable]'), function(field) {
                return field.getFieldLabel();
            }).join(this.labelConnector);
        }
        return label;
    },

    getSubTplData: function() {
        var ret = this.initRenderData();

        Ext.apply(ret, this.subTplData);
        return ret;
    },

    getSubTplMarkup: function() {
        var me = this,
            tpl = me.getTpl('fieldSubTpl'),
            html;

        if (!tpl.renderContent) {
            me.setupRenderTpl(tpl);
        }

        html = tpl.apply(me.getSubTplData());
        return html;
    },

    
    updateLabel: function() {
        var me = this,
            label = me.labelEl;
            
        if (label) {
            me.setFieldLabel(me.getFieldLabel());
        }
    },


    
    onFieldErrorChange: function(field, activeError) {
        if (this.combineErrors) {
            var me = this,
                oldError = me.getActiveError(),
                invalidFields = Ext.Array.filter(me.query('[isFormField]'), function(field) {
                    return field.hasActiveError();
                }),
                newErrors = me.getCombinedErrors(invalidFields);

            if (newErrors) {
                me.setActiveErrors(newErrors);
            } else {
                me.unsetActiveError();
            }

            if (oldError !== me.getActiveError()) {
                me.doComponentLayout();
            }
        }
    },

    
    getCombinedErrors: function(invalidFields) {
        var errors = [],
            f,
            fLen   = invalidFields.length,
            field,
            activeErrors, a, aLen,
            error, label;

        for (f = 0; f < fLen; f++) {
            field = invalidFields[f];
            activeErrors = field.getActiveErrors();
            aLen         = activeErrors.length;

            for (a = 0; a < aLen; a++) {
                error = activeErrors[a];
                label = field.getFieldLabel();

                errors.push((label ? label + ': ' : '') + error);
            }
        }

        return errors;
    },

    getTargetEl: function() {
        return this.containerEl;
    },

    applyTargetCls: function(targetCls) {
        var containerElCls = this.containerElCls;

        this.containerElCls = containerElCls ? containerElCls + ' ' + targetCls : targetCls;
    }
});


Ext.define('Ext.layout.container.CheckboxGroup', {
    extend:  Ext.layout.container.Container ,
    alias: ['layout.checkboxgroup'],

    
    autoFlex: true,

    type: 'checkboxgroup',
    
    createsInnerCt: true,

    childEls: [
        'innerCt'
    ],

    renderTpl: [
        '<table id="{ownerId}-innerCt" class="' + Ext.plainTableCls + '" cellpadding="0"',
            'role="presentation" style="{tableStyle}">',
            '<tbody role="presentation"><tr role="presentation">',
            '<tpl for="columns">',
                '<td class="{parent.colCls}" valign="top" style="{style}" role="presentation">',
                    '{% this.renderColumn(out,parent,xindex-1) %}',
                '</td>',
            '</tpl>',
        '</tr></tbody></table>'
    ],

    lastOwnerItemsGeneration : null,

    beginLayout: function(ownerContext) {
        var me = this,
            columns,
            numCols,
            i, width, cwidth,
            totalFlex = 0, flexedCols = 0,
            autoFlex = me.autoFlex,
            innerCtStyle = me.innerCt.dom.style;

        me.callParent(arguments);

        columns = me.columnNodes;
        ownerContext.innerCtContext = ownerContext.getEl('innerCt', me);

        
        if (!ownerContext.widthModel.shrinkWrap) {
            numCols = columns.length;

            
            if (me.columnsArray) {

                
                for (i = 0; i < numCols; i++) {
                    width = me.owner.columns[i];
                    if (width < 1) {
                        totalFlex += width;
                        flexedCols++;
                    }
                }

                
                for (i = 0; i < numCols; i++) {
                    width = me.owner.columns[i];
                    if (width < 1) {
                        cwidth = ((width / totalFlex) * 100) + '%';
                    } else {
                        cwidth = width + 'px';
                    }
                    columns[i].style.width = cwidth;
                }
            }

            
            else {
                for (i = 0; i < numCols; i++) {
                    
                    
                    
                    cwidth = autoFlex
                        ? (1 / numCols * 100) + '%'
                        : '';
                    columns[i].style.width = cwidth;
                    flexedCols++;
                }
            }

            
            if (!flexedCols) {
                innerCtStyle.tableLayout = 'fixed';
                innerCtStyle.width = '';
            
            } else if (flexedCols < numCols) {
                innerCtStyle.tableLayout = 'fixed';
                innerCtStyle.width = '100%';
            
            } else {
                innerCtStyle.tableLayout = 'auto';
                
                if (autoFlex) {
                    innerCtStyle.width = '100%';
                } else {
                    innerCtStyle.width = '';
                }
            }

        } else {
            innerCtStyle.tableLayout = 'auto';
            innerCtStyle.width = '';
        }
    },

    cacheElements: function () {
        var me = this;

        
        me.callParent();

        me.rowEl = me.innerCt.down('tr');

        
        me.columnNodes = me.rowEl.dom.childNodes;
    },

    
    calculate: function(ownerContext) {
        var me = this,
            targetContext, widthShrinkWrap, heightShrinkWrap, shrinkWrap, table, targetPadding;

        
        
        if (!ownerContext.getDomProp('containerChildrenSizeDone')) {
            me.done = false;
        } else {
            targetContext = ownerContext.innerCtContext;
            widthShrinkWrap = ownerContext.widthModel.shrinkWrap;
            heightShrinkWrap = ownerContext.heightModel.shrinkWrap;
            shrinkWrap = heightShrinkWrap || widthShrinkWrap;
            table = targetContext.el.dom;
            targetPadding = shrinkWrap && targetContext.getPaddingInfo();

            if (widthShrinkWrap) {
                ownerContext.setContentWidth(table.offsetWidth + targetPadding.width, true);
            }

            if (heightShrinkWrap) {
                ownerContext.setContentHeight(table.offsetHeight + targetPadding.height, true);
            }
        }
    },

    doRenderColumn: function (out, renderData, columnIndex) {
        
        

        var me = renderData.$layout,
            owner = me.owner,
            columnCount = renderData.columnCount,
            items = owner.items.items,
            itemCount = items.length,
            item, itemIndex, rowCount, increment, tree;

        
        
        

        if (owner.vertical) {
            
            
            
            
            
            
            
            
            
            

            rowCount = Math.ceil(itemCount / columnCount); 
            itemIndex = columnIndex * rowCount;
            itemCount = Math.min(itemCount, itemIndex + rowCount);
            increment = 1;
        } else {
            
            
            
            
            
            
            
            
            
            

            itemIndex = columnIndex;
            increment = columnCount;
        }

        for ( ; itemIndex < itemCount; itemIndex += increment) {
            item = items[itemIndex];
            me.configureItem(item);
            tree = item.getRenderTree();
            Ext.DomHelper.generateMarkup(tree, out);
        }
    },

    
    getColumnCount: function() {
        var me = this,
            owner = me.owner,
            ownerColumns = owner.columns;

        
        
        if (me.columnsArray) {
            return ownerColumns.length;
        }

        if (Ext.isNumber(ownerColumns)) {
            return ownerColumns;
        }
        return owner.items.length;
    },

    getItemSizePolicy: function (item) {
        return this.autoSizePolicy;
    },

    getRenderData: function () {
        var me = this,
            data = me.callParent(),
            owner = me.owner,
            i, columns = me.getColumnCount(),
            width, column, cwidth,
            autoFlex = me.autoFlex,
            totalFlex = 0, flexedCols = 0;

        
        if (me.columnsArray) {
            for (i=0; i < columns; i++) {
                width = me.owner.columns[i];
                if (width < 1) {
                    totalFlex += width;
                    flexedCols++;
                }
            }
        }

        data.colCls = owner.groupCls;
        data.columnCount = columns;

        data.columns = [];
        for (i = 0; i < columns; i++) {
            column = (data.columns[i] = {});

            if (me.columnsArray) {
                width = me.owner.columns[i];
                if (width < 1) {
                    cwidth = ((width / totalFlex) * 100) + '%';
                } else {
                    cwidth = width + 'px';
                }
                column.style = 'width:' + cwidth;
            } else {
                column.style = 'width:' + (1 / columns * 100) + '%';
                flexedCols++;
            }
        }

        
        data.tableStyle =
            !flexedCols ? 'table-layout:fixed;' :
            (flexedCols < columns) ? 'table-layout:fixed;width:100%' :
            (autoFlex) ? 'table-layout:auto;width:100%' : 'table-layout:auto;';

        return data;
    },

    initLayout: function () {
        var me = this,
            owner = me.owner;

        me.columnsArray = Ext.isArray(owner.columns);
        me.autoColumns = !owner.columns || owner.columns === 'auto';
        me.vertical = owner.vertical;

        me.callParent();
    },

    
    isValidParent: function() {
        return true;
    },

    setupRenderTpl: function (renderTpl) {
        this.callParent(arguments);

        renderTpl.renderColumn = this.doRenderColumn;
    },

    renderChildren: function () {
        var me = this,
            generation = me.owner.items.generation;

        if (me.lastOwnerItemsGeneration !== generation) {
            me.lastOwnerItemsGeneration = generation;
            me.renderItems(me.getLayoutItems());
        }
    },

    
    renderItems : function(items) {
        var me = this,
            itemCount = items.length,
            i,
            item,
            rowCount,
            columnCount,
            rowIndex,
            columnIndex;

        if (itemCount) {
            Ext.suspendLayouts();

            if (me.autoColumns) {
                me.addMissingColumns(itemCount);
            }

            columnCount = me.columnNodes.length;
            rowCount = Math.ceil(itemCount / columnCount);

            for (i = 0; i < itemCount; i++) {
                item = items[i];
                rowIndex = me.getRenderRowIndex(i, rowCount, columnCount);
                columnIndex = me.getRenderColumnIndex(i, rowCount, columnCount);

                if (!item.rendered) {
                    me.renderItem(item, rowIndex, columnIndex);
                } else if (!me.isItemAtPosition(item, rowIndex, columnIndex)) {
                    me.moveItem(item, rowIndex, columnIndex);
                }
            }

            if (me.autoColumns) {
                me.removeExceedingColumns(itemCount);
            }

            Ext.resumeLayouts(true);
        }
    },

    isItemAtPosition : function(item, rowIndex, columnIndex) {
        return item.el.dom === this.getNodeAt(rowIndex, columnIndex);
    },

    getRenderColumnIndex : function(itemIndex, rowCount, columnCount) {
        if (this.vertical) {
            return Math.floor(itemIndex / rowCount);
        } else {
            return itemIndex % columnCount;
        }
    },

    getRenderRowIndex : function(itemIndex, rowCount, columnCount) {
        var me = this;
        if (me.vertical) {
            return itemIndex % rowCount;
        } else {
            return Math.floor(itemIndex / columnCount);
        }
    },

    getNodeAt : function(rowIndex, columnIndex) {
        return this.columnNodes[columnIndex].childNodes[rowIndex];
    },

    addMissingColumns : function(itemsCount) {
        var me = this,
            existingColumnsCount = me.columnNodes.length,
            missingColumnsCount,
            row,
            cls,
            i;
        if (existingColumnsCount < itemsCount) {
            missingColumnsCount = itemsCount - existingColumnsCount;
            row = me.rowEl;
            cls = me.owner.groupCls;
            for (i = 0; i < missingColumnsCount; i++) {
                row.createChild({
                    cls: cls,
                    tag: 'td',
                    vAlign: 'top',
                    role: 'presentation'
                });
            }
        }
    },

    removeExceedingColumns : function(itemsCount) {
        var me = this,
            existingColumnsCount = me.columnNodes.length,
            exceedingColumnsCount,
            row,
            i;
        if (existingColumnsCount > itemsCount) {
            exceedingColumnsCount = existingColumnsCount - itemsCount;
            row = me.rowEl;
            for (i = 0; i < exceedingColumnsCount; i++) {
                row.last().remove();
            }
        }
    },

    
    renderItem : function(item, rowIndex, columnIndex) {
        var me = this;

        me.configureItem(item);
        item.render(Ext.get(me.columnNodes[columnIndex]), rowIndex);
        me.afterRenderItem(item);
    },

    
    moveItem : function(item, rowIndex, columnIndex) {
        var me = this,
            column = me.columnNodes[columnIndex],
            targetNode = column.childNodes[rowIndex];
        column.insertBefore(item.el.dom, targetNode || null);
    }

});


Ext.define('Ext.form.CheckboxManager', {
    extend:  Ext.util.MixedCollection ,
    singleton: true,

    getByName: function(name, formId) {
        return this.filterBy(function(item) {
            return item.name == name && item.getFormId() == formId;
        });
    }
});


Ext.define('Ext.form.field.Checkbox', {
    extend:  Ext.form.field.Base ,
    alias: ['widget.checkboxfield', 'widget.checkbox'],
    alternateClassName: 'Ext.form.Checkbox',
                                                             

    componentLayout: 'field',
    
    
    stretchInputElFixed: false,
    
    childEls: [
        
        'boxLabelEl'
    ],

    
    fieldSubTpl: [
        '<div class="{wrapInnerCls} {noBoxLabelCls}" role="presentation">',
            '<tpl if="labelAlignedBefore">',
                '{beforeBoxLabelTpl}',
                '<label id="{cmpId}-boxLabelEl" {boxLabelAttrTpl} class="{boxLabelCls} {boxLabelCls}-{boxLabelAlign}" for="{id}">',
                    '{beforeBoxLabelTextTpl}',
                    '{boxLabel}',
                    '{afterBoxLabelTextTpl}',
                '</label>',
                '{afterBoxLabelTpl}',
            '</tpl>',
            '<input type="button" id="{id}" role="{role}" {inputAttrTpl}',
                '<tpl if="tabIdx"> tabIndex="{tabIdx}"</tpl>',
                '<tpl if="disabled"> disabled="disabled"</tpl>',
                '<tpl if="fieldStyle"> style="{fieldStyle}"</tpl>',
                ' class="{fieldCls} {typeCls} {inputCls} {childElCls} {afterLabelCls}" autocomplete="off" hidefocus="true" />',
            '<tpl if="boxLabel && !labelAlignedBefore">',
                '{beforeBoxLabelTpl}',
                '<label id="{cmpId}-boxLabelEl" {boxLabelAttrTpl} class="{boxLabelCls} {boxLabelCls}-{boxLabelAlign}" for="{id}">',
                    '{beforeBoxLabelTextTpl}',
                    '{boxLabel}',
                    '{afterBoxLabelTextTpl}',
                '</label>',
                '{afterBoxLabelTpl}',
            '</tpl>',
        '</div>',
        {
            disableFormats: true,
            compiled: true
        }
    ],

    subTplInsertions: [
        
        'beforeBoxLabelTpl',

        
        'afterBoxLabelTpl',

        
        'beforeBoxLabelTextTpl',

        
        'afterBoxLabelTextTpl',

        
        'boxLabelAttrTpl',

        
        'inputAttrTpl'
    ],

    
    isCheckbox: true,

    
    focusCls: 'form-checkbox-focus',

    
    
    
    extraFieldBodyCls: Ext.baseCSSPrefix + 'form-cb-wrap',

    
    checked: false,

    
    checkedCls: Ext.baseCSSPrefix + 'form-cb-checked',

    

    
    boxLabelCls: Ext.baseCSSPrefix + 'form-cb-label',

    
    boxLabelAlign: 'after',

    afterLabelCls: Ext.baseCSSPrefix + 'form-cb-after',

    wrapInnerCls: Ext.baseCSSPrefix + 'form-cb-wrap-inner',

    
    
    
    
    noBoxLabelCls: Ext.baseCSSPrefix + 'form-cb-wrap-inner-no-box-label',

    
    inputValue: 'on',

    

    

    

    
    checkChangeEvents: [],
    inputType: 'checkbox',
    ariaRole: 'checkbox',
    
    
    onRe: /^on$/i,

    
    inputCls: Ext.baseCSSPrefix + 'form-cb',

    initComponent: function() {
        var me = this,
            value = me.value;
            
        if (value !== undefined) {
            me.checked = me.isChecked(value, me.inputValue);
        }
        
        me.callParent(arguments);
        me.getManager().add(me);
    },

    initValue: function() {
        var me = this,
            checked = !!me.checked;

        
        me.originalValue = me.lastValue = checked;

        
        me.setValue(checked);
    },

    getElConfig: function() {
        var me = this;

        
        if (me.isChecked(me.rawValue, me.inputValue)) {
            me.addCls(me.checkedCls);
        }

        return me.callParent();
    },

    getSubTplData: function() {
        var me = this,
            boxLabel = me.boxLabel,
            boxLabelAlign = me.boxLabelAlign,
            labelAlignedBefore = boxLabel && boxLabelAlign === 'before';

        return Ext.apply(me.callParent(), {
            disabled: me.readOnly || me.disabled,
            wrapInnerCls: me.wrapInnerCls,
            boxLabel: boxLabel,
            boxLabelCls: me.boxLabelCls,
            boxLabelAlign: boxLabelAlign,
            labelAlignedBefore: labelAlignedBefore,
            afterLabelCls: labelAlignedBefore ? me.afterLabelCls : '',
            noBoxLabelCls: !boxLabel ? me.noBoxLabelCls : '',
            role: me.ariaRole
        });
    },

    initEvents: function() {
        var me = this;
        me.callParent();
        me.mon(me.inputEl, 'click', me.onBoxClick, me);
    },
    
    
    setBoxLabel: function(boxLabel){
        var me = this;
        
        me.boxLabel = boxLabel;
        if (me.rendered) {
            me.boxLabelEl.update(boxLabel);
        }
    },

    
    onBoxClick: function(e) {
        var me = this;
        if (!me.disabled && !me.readOnly) {
            this.setValue(!this.checked);
        }
    },

    
    getRawValue: function() {
        return this.checked;
    },

    
    getValue: function() {
        return this.checked;
    },

    
    getSubmitValue: function() {
        var unchecked = this.uncheckedValue,
            uncheckedVal = Ext.isDefined(unchecked) ? unchecked : null;
        return this.checked ? this.inputValue : uncheckedVal;
    },

    isChecked: function(rawValue, inputValue) {
        return (rawValue === true || rawValue === 'true' || rawValue === '1' || rawValue === 1 ||
                      (((Ext.isString(rawValue) || Ext.isNumber(rawValue)) && inputValue) ? rawValue == inputValue : this.onRe.test(rawValue)));
    },

    
    setRawValue: function(value) {
        var me = this,
            inputEl = me.inputEl,
            checked = me.isChecked(value, me.inputValue);

        if (inputEl) {
            me[checked ? 'addCls' : 'removeCls'](me.checkedCls);
        }

        me.checked = me.rawValue = checked;
        return checked;
    },

    
    setValue: function(checked) {
        var me = this,
            boxes, i, len, box;

        
        
        
        
        if (Ext.isArray(checked)) {
            boxes = me.getManager().getByName(me.name, me.getFormId()).items;
            len = boxes.length;

            for (i = 0; i < len; ++i) {
                box = boxes[i];
                box.setValue(Ext.Array.contains(checked, box.inputValue));
            }
        } else {
            me.callParent(arguments);
        }

        return me;
    },

    
    valueToRaw: function(value) {
        
        return value;
    },

    
    onChange: function(newVal, oldVal) {
        var me = this,
            handler = me.handler;
        if (handler) {
            handler.call(me.scope || me, me, newVal);
        }
        me.callParent(arguments);
    },
    
    resetOriginalValue: function( fromBoxInGroup){
        var me = this,
            boxes,
            box,
            len,
            i;
            
        
        if (!fromBoxInGroup) {
            boxes = me.getManager().getByName(me.name, me.getFormId()).items;
            len  = boxes.length;
            
            for (i = 0; i < len; ++i) {
                box = boxes[i];
                if (box !== me) {
                    boxes[i].resetOriginalValue(true);
                }
            }
        }
        me.callParent();
    },

    
    beforeDestroy: function(){
        this.callParent();
        this.getManager().removeAtKey(this.id);
    },

    
    getManager: function() {
        return Ext.form.CheckboxManager;
    },

    onEnable: function() {
        var me = this,
            inputEl = me.inputEl;
        me.callParent();
        if (inputEl) {
            
            inputEl.dom.disabled = me.readOnly;
        }
    },

    setReadOnly: function(readOnly) {
        var me = this,
            inputEl = me.inputEl;
        if (inputEl) {
            
            inputEl.dom.disabled = !!readOnly || me.disabled;
        }
        me.callParent(arguments);
    },

    getFormId: function(){
        var me = this,
            form;

        if (!me.formId) {
            form = me.up('form');
            if (form) {
                me.formId = form.id;
            }
        }
        return me.formId;
    }
});


Ext.define('Ext.form.CheckboxGroup', {
    extend: Ext.form.FieldContainer ,
    mixins: {
        field:  Ext.form.field.Field 
    },
    alias: 'widget.checkboxgroup',
               
                                             
                                  
                             
      

    

    

    
    columns : 'auto',

    
    vertical : false,

    
    allowBlank : true,

    
    
    blankText : "You must select at least one item in this group",
    

    
    defaultType : 'checkboxfield',

    
    groupCls : Ext.baseCSSPrefix + 'form-check-group',

    
    extraFieldBodyCls: Ext.baseCSSPrefix + 'form-checkboxgroup-body',

    
    layout: 'checkboxgroup',

    componentCls: Ext.baseCSSPrefix + 'form-checkboxgroup',
    
    ariaRole: 'group',

    initComponent: function() {
        var me = this;
        me.callParent();
        me.initField();
    },

    
    initValue: function() {
        var me = this,
            valueCfg = me.value;
        me.originalValue = me.lastValue = valueCfg || me.getValue();
        if (valueCfg) {
            me.setValue(valueCfg);
        }
    },

    
    onAdd: function(item) {
        var me = this,
            items,
            len, i;

        if (item.isCheckbox) {
            me.mon(item, 'change', me.checkChange, me);
        } else if (item.isContainer) {
            items = item.items.items;
            for (i = 0, len = items.length; i < len; i++) {
                me.onAdd(items[i]);
            }
        }
        me.callParent(arguments);
    },

    onRemove: function(item) {
        var me = this,
            items,
            len, i;

        if (item.isCheckbox) {
            me.mun(item, 'change', me.checkChange, me);
        } else if (item.isContainer) {
            items = item.items.items;
            for (i = 0, len = items.length; i < len; i++) {
                me.onRemove(items[i]);
            }
        }
        me.callParent(arguments);
    },

    
    isEqual: function(value1, value2) {
        var toQueryString = Ext.Object.toQueryString;
        return toQueryString(value1) === toQueryString(value2);
    },

    
    getErrors: function() {
        var errors = [];
        if (!this.allowBlank && Ext.isEmpty(this.getChecked())) {
            errors.push(this.blankText);
        }
        return errors;
    },

    
    getBoxes: function(query) {
        return this.query('[isCheckbox]' + (query||''));
    },

    
    eachBox: function(fn, scope) {
        Ext.Array.forEach(this.getBoxes(), fn, scope || this);
    },

    
    getChecked: function() {
        return this.getBoxes('[checked]');
    },

    
    isDirty: function(){
        var boxes = this.getBoxes(),
            b ,
            bLen  = boxes.length;

        for (b = 0; b < bLen; b++) {
            if (boxes[b].isDirty()) {
                return true;
            }
        }
    },

    
    setReadOnly: function(readOnly) {
        var boxes = this.getBoxes(),
            b,
            bLen  = boxes.length;

        for (b = 0; b < bLen; b++) {
            boxes[b].setReadOnly(readOnly);
        }

        this.readOnly = readOnly;
    },

    
    reset: function() {
        var me = this,
            hadError = me.hasActiveError(),
            preventMark = me.preventMark;
        me.preventMark = true;
        me.batchChanges(function() {
            var boxes = me.getBoxes(),
                b,
                bLen  = boxes.length;

            for (b = 0; b < bLen; b++) {
                boxes[b].reset();
            }
        });
        me.preventMark = preventMark;
        me.unsetActiveError();
        if (hadError) {
            me.updateLayout();
        }
    },

    resetOriginalValue: function(){
        var me    = this,
            boxes = me.getBoxes(),
            b,
            bLen  = boxes.length;

        for (b = 0; b < bLen; b++) {
            boxes[b].resetOriginalValue();
        }

        me.originalValue = me.getValue();
        me.checkDirty();
    },


    
    setValue: function(value) {
        var me    = this,
            boxes = me.getBoxes(),
            b,
            bLen  = boxes.length,
            box, name,
            cbValue;

        me.batchChanges(function() {
            for (b = 0; b < bLen; b++) {
                box = boxes[b];
                name = box.getName();
                cbValue = false;

                if (value && value.hasOwnProperty(name)) {
                    if (Ext.isArray(value[name])) {
                        cbValue = Ext.Array.contains(value[name], box.inputValue);
                    } else {
                        
                        cbValue = value[name];
                    }
                }

                box.setValue(cbValue);
            }
        });
        return me;
    },


    
    getValue: function() {
        var values = {},
            boxes  = this.getBoxes(),
            b,
            bLen   = boxes.length,
            box, name, inputValue, bucket;

        for (b = 0; b < bLen; b++) {
            box        = boxes[b];
            name       = box.getName();
            inputValue = box.inputValue;

            if (box.getValue()) {
                if (values.hasOwnProperty(name)) {
                    bucket = values[name];
                    if (!Ext.isArray(bucket)) {
                        bucket = values[name] = [bucket];
                    }
                    bucket.push(inputValue);
                } else {
                    values[name] = inputValue;
                }
            }
        }

        return values;
    },

    
    getSubmitData: function() {
        return null;
    },

    
    getModelData: function() {
        return null;
    },

    validate: function() {
        var me = this,
            errors,
            isValid,
            wasValid;

        if (me.disabled) {
            isValid = true;
        } else {
            errors = me.getErrors();
            isValid = Ext.isEmpty(errors);
            wasValid = me.wasValid;
            if (isValid) {
                me.unsetActiveError();
            } else {
                me.setActiveError(errors);
            }
        }
        if (isValid !== wasValid) {
            me.wasValid = isValid;
            me.fireEvent('validitychange', me, isValid);
            me.updateLayout();
        }

        return isValid;
    }

}, function() {

    this.borrow(Ext.form.field.Base, ['markInvalid', 'clearInvalid', 'setError']);

});



Ext.define('Ext.form.FieldSet', {
    extend:  Ext.container.Container ,
    mixins: {
        fieldAncestor:  Ext.form.FieldAncestor 
    },
    alias: 'widget.fieldset',
                                                                                                                        

    

    

    

    

    
    collapsed: false,

    
    toggleOnTitleClick : true,

    

    
    baseCls: Ext.baseCSSPrefix + 'fieldset',

    
    layout: 'anchor',

    componentLayout: 'fieldset',
    
    ariaRole: null,

    autoEl: 'fieldset',

    childEls: [
        'body'
    ],

    renderTpl: [
        '{%this.renderLegend(out,values);%}',
        '<div id="{id}-body" class="{baseCls}-body {bodyTargetCls}" role="presentation"<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>>',
            '{%this.renderContainer(out,values);%}',
        '</div>'
    ],

    stateEvents : [ 'collapse', 'expand' ],

    maskOnDisable: false,

    beforeDestroy: function(){
        var me = this,
            legend = me.legend;

        if (legend) {
            
            delete legend.ownerCt;
            legend.destroy();
            me.legend = null;
        }
        me.callParent();
    },

    initComponent: function() {
        var me = this,
            baseCls = me.baseCls;
                
        me.initFieldAncestor();

        me.callParent();

        
        
        
        
        
        
        
        
        
        
        me.layout.managePadding = me.layout.manageOverflow = false;

        me.addEvents(

            
            "beforeexpand",

            
            "beforecollapse",

            
            "expand",

            
            "collapse"
        );

        if (me.collapsed) {
            me.addCls(baseCls + '-collapsed');
            me.collapse();
        }
        if (me.title || me.checkboxToggle || me.collapsible) {
            me.addTitleClasses();
            me.legend = Ext.widget(me.createLegendCt());
        }
        me.initMonitor();
    },

    initPadding: function(targetEl) {
        var me = this,
            body = me.getProtoBody(),
            padding = me.padding,
            bodyPadding;

        if (padding !== undefined) {
            if (Ext.isIEQuirks || Ext.isIE8m) {
                
                
                padding = me.parseBox(padding);
                bodyPadding = Ext.Element.parseBox(0);
                bodyPadding.top = padding.top;
                padding.top = 0;
                body.setStyle('padding', me.unitizeBox(bodyPadding));
            }

            targetEl.setStyle('padding', me.unitizeBox(padding));
        }
    },

    getProtoBody: function () {
        var me = this,
            body = me.protoBody;

        if (!body) {
            me.protoBody = body = new Ext.util.ProtoElement({
                styleProp: 'bodyStyle',
                styleIsText: true
            });
        }

        return body;
    },

    
    initRenderData: function() {
        var me = this,
            data = me.callParent();

        data.bodyTargetCls = me.bodyTargetCls;
        me.protoBody.writeTo(data);
        delete me.protoBody;

        return data;
    },

    getState: function () {
        var state = this.callParent();

        state = this.addPropertyToState(state, 'collapsed');

        return state;
    },

    afterCollapse: Ext.emptyFn,
    afterExpand: Ext.emptyFn,

    collapsedHorizontal: function () {
        return true;
    },

    collapsedVertical: function () {
        return true;
    },

    createLegendCt: function () {
        var me = this,
            items = [],
            legend = {
                xtype: 'container',
                baseCls: me.baseCls + '-header',
                id: me.id + '-legend',
                autoEl: 'legend',
                ariaRole: null,
                ariaLabelledBy: '.' + me.baseCls + '-header-text',
                items: items,
                ownerCt: me,
                shrinkWrap: true,
                ownerLayout: me.componentLayout
            };

        
        if (me.checkboxToggle) {
            items.push(me.createCheckboxCmp());
        } else if (me.collapsible) {
            
            items.push(me.createToggleCmp());
        }

        
        items.push(me.createTitleCmp());

        return legend;
    },

    
    createTitleCmp: function() {
        var me  = this,
            cfg = {
                xtype : 'component',
                html  : me.title,
                cls   : me.baseCls + '-header-text',
                id    : me.id + '-legendTitle'
            };

        if (me.collapsible && me.toggleOnTitleClick) {
            cfg.listeners = {
                click : {
                    element: 'el',
                    scope : me,
                    fn : me.toggle
                }
            };
            cfg.cls += ' ' + me.baseCls + '-header-text-collapsible';
        }

        return (me.titleCmp = Ext.widget(cfg));
    },

    

    
    createCheckboxCmp: function() {
        var me = this,
            suffix = '-checkbox';

        me.checkboxCmp = Ext.widget({
            xtype: 'checkbox',
            hideEmptyLabel: true,
            name: me.checkboxName || me.id + suffix,
            cls: me.baseCls + '-header' + suffix,
            id: me.id + '-legendChk',
            checked: !me.collapsed,
            msgTarget: 'none',
            listeners: {
                change: me.onCheckChange,
                scope: me
            }
        });
        return me.checkboxCmp;
    },

    

    
    createToggleCmp: function() {
        var me = this;
        me.toggleCmp = Ext.widget({
            xtype: 'tool',
            height: 15,
            width: 15,
            type: 'toggle',
            handler: me.toggle,
            id: me.id + '-legendToggle',
            scope: me
        });
        return me.toggleCmp;
    },

    doRenderLegend: function (out, renderData) {
        
        

        var me = renderData.$comp,
            legend = me.legend,
            tree;
            
        
        if (legend) {
            legend.ownerLayout.configureItem(legend);
            tree = legend.getRenderTree();
            Ext.DomHelper.generateMarkup(tree, out);
        }
    },

    finishRender: function () {
        var legend = this.legend;

        this.callParent();

        if (legend) {
            legend.finishRender();
        }
    },

    getCollapsed: function () {
        return this.collapsed ? 'top' : false;
    },

    getCollapsedDockedItems: function () {
        var legend = this.legend;

        return legend ? [ legend ] : [];
    },

    
    setTitle: function(title) {
        var me = this,
            legend = me.legend,
            baseCls = me.baseCls;
            
        me.title = title;
        if (me.rendered) {
            if (!legend) {
                me.legend = legend = Ext.widget(me.createLegendCt());
                me.addTitleClasses();
                legend.ownerLayout.configureItem(legend);
                legend.render(me.el, 0);
            }
            me.titleCmp.update(title);
        } else if (legend) {
            me.titleCmp.update(title);
        } else {
            me.addTitleClasses();
            me.legend = Ext.widget(me.createLegendCt());
        }
        return me;
    },
    
    addTitleClasses: function(){
        var me = this,
            title = me.title,
            baseCls = me.baseCls;
            
        if (title) {
            me.addCls(baseCls + '-with-title');
        }
        
        if (title || me.checkboxToggle || me.collapsible) {
            me.addCls(baseCls + '-with-legend');
        }
    },

    applyTargetCls: function(targetCls) {
        this.bodyTargetCls = targetCls;
    },

    getTargetEl : function() {
        return this.body || this.frameBody || this.el;
    },

    getDefaultContentTarget: function() {
        return this.body;
    },

    
    expand : function(){
        return this.setExpanded(true);
    },

    
    collapse : function() {
        return this.setExpanded(false);
    },

    
    setExpanded: function(expanded) {
        var me = this,
            checkboxCmp = me.checkboxCmp,
            operation = expanded ? 'expand' : 'collapse';

        if (!me.rendered || me.fireEvent('before' + operation, me) !== false) {
            expanded = !!expanded;

            if (checkboxCmp) {
                checkboxCmp.setValue(expanded);
            }

            if (expanded) {
                me.removeCls(me.baseCls + '-collapsed');
            } else {
                me.addCls(me.baseCls + '-collapsed');
            }
            me.collapsed = !expanded;
            if (expanded) {
                delete me.getHierarchyState().collapsed;
            } else {
                me.getHierarchyState().collapsed = true;
            }
            if (me.rendered) {
                
                
                
                me.updateLayout({ isRoot: false });
                me.fireEvent(operation, me);
            }
        }
        return me;
    },
    
    getRefItems: function(deep) {
        var refItems = this.callParent(arguments),
            legend = this.legend;

        
        if (legend) {
            refItems.unshift(legend);
            if (deep) {
                refItems.unshift.apply(refItems, legend.getRefItems(true));
            }
        }
        return refItems;
    },

    
    toggle: function() {
        this.setExpanded(!!this.collapsed);
    },

    
    onCheckChange: function(cmp, checked) {
        this.setExpanded(checked);
    },

    setupRenderTpl: function (renderTpl) {
        this.callParent(arguments);

        renderTpl.renderLegend = this.doRenderLegend;
    }
});


Ext.define('Ext.form.Label', {
    extend: Ext.Component ,
    alias: 'widget.label',
                                  

    autoEl: 'label',

    
    
    
    
    maskOnDisable: false,

    getElConfig: function(){
        var me = this;

        me.html = me.text ? Ext.util.Format.htmlEncode(me.text) : (me.html || '');
        return Ext.apply(me.callParent(), {
            htmlFor: me.forId || ''
        });
    },

    
    setText : function(text, encode){
        var me = this;
        
        encode = encode !== false;
        if(encode) {
            me.text = text;
            delete me.html;
        } else {
            me.html = text;
            delete me.text;
        }
        
        if(me.rendered){
            me.el.dom.innerHTML = encode !== false ? Ext.util.Format.htmlEncode(text) : text;
            me.updateLayout();
        }
        return me;
    }
});



Ext.define('Ext.form.Panel', {
    extend: Ext.panel.Panel ,
    mixins: {
        fieldAncestor:  Ext.form.FieldAncestor 
    },
    alias: 'widget.form',
    alternateClassName: ['Ext.FormPanel', 'Ext.form.FormPanel'],
                                                        

    

    

    
    layout: 'anchor',

    ariaRole: 'form',
    
    basicFormConfigs: [
        
        'api', 
        
        'baseParams', 
        
        'errorReader', 
        
        'jsonSubmit',
        
        'method', 
        
        'paramOrder',
        
        'paramsAsHash',
        
        'reader',
        
        'standardSubmit',
        
        'timeout',
        
        'trackResetOnLoad',
        
        'url',
        
        'waitMsgTarget',
        
        'waitTitle'
    ],

    initComponent: function() {
        var me = this;

        if (me.frame) {
            me.border = false;
        }

        me.initFieldAncestor();
        me.callParent();

        me.relayEvents(me.form, [
            
            'beforeaction',
            
            'actionfailed',
            
            'actioncomplete',
            
            'validitychange',
            
            'dirtychange'
        ]);

        
        if (me.pollForChanges) {
            me.startPolling(me.pollInterval || 500);
        }
    },

    initItems: function() {
        
        this.callParent();
        this.initMonitor();
        this.form = this.createForm();
    },

    
    afterFirstLayout: function() {
        this.callParent(arguments);
        this.form.initialize();
    },

    
    createForm: function() {
        var cfg = {},
            props = this.basicFormConfigs,
            len = props.length,
            i = 0,
            prop;
            
        for (; i < len; ++i) {
            prop = props[i];
            cfg[prop] = this[prop];
        }
        return new Ext.form.Basic(this, cfg);
    },

    
    getForm: function() {
        return this.form;
    },

    
    loadRecord: function(record) {
        return this.getForm().loadRecord(record);
    },

    
    getRecord: function() {
        return this.getForm().getRecord();
    },
    
    
    updateRecord: function(record) {
        return this.getForm().updateRecord(record);
    },

    
    getValues: function(asString, dirtyOnly, includeEmptyText, useDataValues) {
        return this.getForm().getValues(asString, dirtyOnly, includeEmptyText, useDataValues);
    },
    
    
    isDirty: function () {
        return this.form.isDirty();
    },
    
    
    isValid: function () {
       return this.form.isValid();
    },
    
    
    hasInvalidField: function () {
        return this.form.hasInvalidField();
    },

    beforeDestroy: function() {
        this.stopPolling();
        this.form.destroy();
        this.callParent();
    },

    
    load: function(options) {
        this.form.load(options);
    },

    
    submit: function(options) {
        this.form.submit(options);
    },

    
    startPolling: function(interval) {
        this.stopPolling();
        var task = new Ext.util.TaskRunner(interval);
        task.start({
            interval: 0,
            run: this.checkChange,
            scope: this
        });
        this.pollTask = task;
    },

    
    stopPolling: function() {
        var task = this.pollTask;
        if (task) {
            task.stopAll();
            delete this.pollTask;
        }
    },

    
    checkChange: function() {
        var fields = this.form.getFields().items,
            f,
            fLen   = fields.length;

        for (f = 0; f < fLen; f++) {
            fields[f].checkChange();
        }
    }
});


Ext.define('Ext.form.RadioManager', {
    extend:  Ext.util.MixedCollection ,
    singleton: true,

    getByName: function(name, formId) {
        return this.filterBy(function(item) {
            return item.name == name && item.getFormId() == formId;
        });
    },

    getWithValue: function(name, value, formId) {
        return this.filterBy(function(item) {
            return item.name == name && item.inputValue == value && item.getFormId() == formId;
        });
    },

    getChecked: function(name, formId) {
        return this.findBy(function(item) {
            return item.name == name && item.checked && item.getFormId() == formId;
        });
    }
});


Ext.define('Ext.form.field.Radio', {
    extend: Ext.form.field.Checkbox ,
    alias: ['widget.radiofield', 'widget.radio'],
    alternateClassName: 'Ext.form.Radio',
                                        

    
    isRadio: true,

    
    focusCls: 'form-radio-focus',

    

    
    inputType: 'radio',
    ariaRole: 'radio',
    
    formId: null,

    
    getGroupValue: function() {
        var selected = this.getManager().getChecked(this.name, this.getFormId());
        return selected ? selected.inputValue : null;
    },

    
    onBoxClick: function(e) {
        var me = this;
        if (!me.disabled && !me.readOnly) {
            this.setValue(true);
        }
    },
    
    onRemoved: function(){
        this.callParent(arguments);
        this.formId = null;
    },

    
    setValue: function(v) {
        var me = this,
            active;

        if (Ext.isBoolean(v)) {
            me.callParent(arguments);
        } else {
            active = me.getManager().getWithValue(me.name, v, me.getFormId()).getAt(0);
            if (active) {
                active.setValue(true);
            }
        }
        return me;
    },

    
    getSubmitValue: function() {
        return this.checked ? this.inputValue : null;
    },

    getModelData: function() {
        var o = this.callParent(arguments);
        if (o) {
            o[this.getName()] = this.getSubmitValue();
        }
        return o;
    },

    
    onChange: function(newVal, oldVal) {
        var me = this,
            r, rLen, radio, radios;

        me.callParent(arguments);

        if (newVal) {
            radios = me.getManager().getByName(me.name, me.getFormId()).items;
            rLen   = radios.length;

            for (r = 0; r < rLen; r++) {
                radio = radios[r];

                if (radio !== me) {
                    radio.setValue(false);
                }
            }
        }
    },

    
    getManager: function() {
        return Ext.form.RadioManager;
    }
});


Ext.define('Ext.form.RadioGroup', {
    extend:  Ext.form.CheckboxGroup ,
    alias: 'widget.radiogroup',

               
                              
      

    
    
    allowBlank : true,
    
    
    blankText : 'You must select one item in this group',
    

    
    defaultType : 'radiofield',

    
    groupCls : Ext.baseCSSPrefix + 'form-radio-group',
    
    ariaRole: 'radiogroup',

    getBoxes: function(query) {
        return this.query('[isRadio]' + (query||''));
    },
    
    checkChange: function() {
        var value = this.getValue(),
            key = Ext.Object.getKeys(value)[0];
            
        
        
        if (Ext.isArray(value[key])) {
            return;
        }
        this.callParent(arguments);    
    },

    
    setValue: function(value) {
        var cbValue, first, formId, radios,
            i, len, name;

        if (Ext.isObject(value)) {
            for (name in value) {
                if (value.hasOwnProperty(name)) {
                    cbValue = value[name];
                    first = this.items.first();
                    formId = first ? first.getFormId() : null;
                    radios = Ext.form.RadioManager.getWithValue(name, cbValue, formId).items;
                    len = radios.length;

                    for (i = 0; i < len; ++i) {
                        radios[i].setValue(true);
                    }
                }
            }
        }
        return this;
    }
});


Ext.define('Ext.form.action.DirectLoad', {
    extend: Ext.form.action.Load ,
                                     
    alternateClassName: 'Ext.form.Action.DirectLoad',
    alias: 'formaction.directload',

    type: 'directload',

    run: function() {
        var me = this,
            form = me.form,
            api = form.api,
            fn = api.load,
            method, args;

        if (typeof fn !== 'function') {
            
            api.load = fn = Ext.direct.Manager.parseMethod(fn);

        }
        
        method = fn.directCfg.method;
        args = method.getArgs(me.getParams(), form.paramOrder, form.paramsAsHash);
            
        args.push(me.onComplete, me);
        fn.apply(window, args);
    },

    
    
    
    processResponse: function(result) {
        return (this.result = result);
    },

    onComplete: function(data, response) {
        if (data) {
            this.onSuccess(data);
        } else {
            this.onFailure(null);
        }
    }
});




Ext.define('Ext.form.action.DirectSubmit', {
    extend: Ext.form.action.Submit ,
                                     
    alternateClassName: 'Ext.form.Action.DirectSubmit',
    alias: 'formaction.directsubmit',

    type: 'directsubmit',

    doSubmit: function() {
        var me = this,
            form = me.form,
            api = form.api,
            fn = api.submit,
            callback = Ext.Function.bind(me.onComplete, me),
            formInfo = me.buildForm(),
            options;
        
        if (typeof fn !== 'function') {
            
            api.submit = fn = Ext.direct.Manager.parseMethod(fn);
            me.cleanup(formInfo);

        }
        
        if (me.timeout || form.timeout) {
            options = {
                timeout: me.timeout * 1000 || form.timeout * 1000
            };
        }
        
        fn.call(window, formInfo.formEl, callback, me, options);
        me.cleanup(formInfo);
    },

    
    
    
    processResponse: function(result) {
        return (this.result = result);
    },
    
    onComplete: function(data, response){
        if (data) {
            this.onSuccess(data);
        } else {
            this.onFailure(null);
        }
    }
});


Ext.define('Ext.form.action.StandardSubmit', {
    extend: Ext.form.action.Submit ,
    alias: 'formaction.standardsubmit',

    

    
    doSubmit: function() {
        var formInfo = this.buildForm();
        formInfo.formEl.submit();
        this.cleanup(formInfo);
    }

});


Ext.define('Ext.layout.component.field.Trigger', {

    

    alias: 'layout.triggerfield',

    extend:  Ext.layout.component.field.Field ,

    

    type: 'triggerfield',

    
    
    setWidthInDom: true,

    
    borderWidths: {},

    beginLayout: function(ownerContext) {
        var me = this,
            owner = me.owner,
            flags;

        ownerContext.triggerWrap = ownerContext.getEl('triggerWrap');

        me.callParent(arguments);

        
        flags = owner.getTriggerStateFlags();
        if (flags != owner.lastTriggerStateFlags) {
            owner.lastTriggerStateFlags = flags;
            me.updateEditState();
        }
    },
    
    beginLayoutCycle: function(ownerContext){
        this.callParent(arguments);
        
        
        if (ownerContext.widthModel.shrinkWrap && !this.owner.inputWidth) {
            ownerContext.inputContext.el.setStyle('width', '');
        }    
    },

    beginLayoutFixed: function (ownerContext, width, suffix) {
        var me = this,
            owner = ownerContext.target,
            ieInputWidthAdjustment = me.ieInputWidthAdjustment || 0,
            inputWidth = '100%',
            triggerWrap = owner.triggerWrap;

        me.callParent(arguments);

        owner.inputCell.setStyle('width', '100%');
        if(ieInputWidthAdjustment) {
            me.adjustIEInputPadding(ownerContext);
            if(suffix === 'px') {
                if (owner.inputWidth) {
                    inputWidth = owner.inputWidth - me.getExtraWidth(ownerContext);
                } else {
                    inputWidth = width - ieInputWidthAdjustment - me.getExtraWidth(ownerContext);
                }
                inputWidth += 'px';
            }
        }
        owner.inputEl.setStyle('width', inputWidth);
        inputWidth = owner.inputWidth;
        if (inputWidth) {
            triggerWrap.setStyle('width', inputWidth + (ieInputWidthAdjustment) + 'px');
        } else {
            triggerWrap.setStyle('width', width + suffix);
        }
        triggerWrap.setStyle('table-layout', 'fixed');
    },

    adjustIEInputPadding: function(ownerContext) {
        
        this.owner.inputCell.setStyle('padding-right', this.ieInputWidthAdjustment + 'px');
    },

    
    getExtraWidth: function(ownerContext) {
        var me = this,
            owner = me.owner,
            borderWidths = me.borderWidths,
            ui = owner.ui + owner.triggerEl.getCount();

        if (!(ui in borderWidths)) {
            borderWidths[ui] = ownerContext.triggerWrap.getBorderInfo().width
        }
        return borderWidths[ui] + owner.getTriggerWidth();
    },

    beginLayoutShrinkWrap: function (ownerContext) {
        var owner = ownerContext.target,
            emptyString = '',
            inputWidth = owner.inputWidth,
            triggerWrap = owner.triggerWrap;

        this.callParent(arguments);

        if (inputWidth) {
            triggerWrap.setStyle('width', inputWidth + 'px');
            inputWidth = (inputWidth - this.getExtraWidth(ownerContext)) + 'px';
            owner.inputEl.setStyle('width', inputWidth);
            owner.inputCell.setStyle('width', inputWidth);
        } else {
            owner.inputCell.setStyle('width', emptyString);
            owner.inputEl.setStyle('width', emptyString);
            triggerWrap.setStyle('width', emptyString);
            triggerWrap.setStyle('table-layout', 'auto');
        }
    },

    getTextWidth: function () {
        var me = this,
            owner = me.owner,
            inputEl = owner.inputEl,
            value;

        
        value = (inputEl.dom.value || (owner.hasFocus ? '' : owner.emptyText) || '') + owner.growAppend;
        return inputEl.getTextWidth(value);
    },
    
    publishOwnerWidth: function(ownerContext, width) {
        var owner = this.owner;
        this.callParent(arguments);
        if (!owner.grow && !owner.inputWidth) {
            width -= this.getExtraWidth(ownerContext);
            if (owner.labelAlign != 'top') {
                width -= owner.getLabelWidth();
            }
            ownerContext.inputContext.setWidth(width);
        }    
    },

    publishInnerHeight: function(ownerContext, height) {
        ownerContext.inputContext.setHeight(height - this.measureLabelErrorHeight(ownerContext));
    },

    measureContentWidth: function (ownerContext) {
        var me = this,
            owner = me.owner,
            width = me.callParent(arguments),
            inputContext = ownerContext.inputContext,
            calcWidth, max, min;

        if (owner.grow && !ownerContext.state.growHandled) {
            calcWidth = me.getTextWidth() + ownerContext.inputContext.getFrameInfo().width;

            max = owner.growMax;
            min = Math.min(max, width);
            max = Math.max(owner.growMin, max, min);

            
            calcWidth = Ext.Number.constrain(calcWidth, owner.growMin, max);
            inputContext.setWidth(calcWidth);
            ownerContext.state.growHandled = true;
            
            
            inputContext.domBlock(me, 'width');
            width = NaN;
        } else if (!owner.inputWidth) {
            width -= me.getExtraWidth(ownerContext);
        }
        return width;
    },

    updateEditState: function() {
        var me = this,
            owner = me.owner,
            inputEl = owner.inputEl,
            noeditCls = Ext.baseCSSPrefix + 'trigger-noedit',
            displayed,
            readOnly;

        if (me.owner.readOnly) {
            inputEl.addCls(noeditCls);
            readOnly = true;
            displayed = false;
        } else {
            if (me.owner.editable) {
                inputEl.removeCls(noeditCls);
                readOnly = false;
            } else {
                inputEl.addCls(noeditCls);
                readOnly = true;
            }
            displayed = !me.owner.hideTrigger;
        }

        owner.triggerCell.setDisplayed(displayed);
        inputEl.dom.readOnly = readOnly;
    }
});


Ext.define('Ext.form.field.Trigger', {
    extend: Ext.form.field.Text ,
    alias: ['widget.triggerfield', 'widget.trigger'],
                                                                                                 
    alternateClassName: ['Ext.form.TriggerField', 'Ext.form.TwinTriggerField', 'Ext.form.Trigger'],

    childEls: [
        
        { name: 'triggerCell', select: '.' + Ext.baseCSSPrefix + 'trigger-cell' },
        { name: 'triggerEl', select: '.' + Ext.baseCSSPrefix + 'form-trigger' },

        
        'triggerWrap',

        
        'inputCell'
    ],

    

    
    triggerBaseCls: Ext.baseCSSPrefix + 'form-trigger',

    
    triggerWrapCls: Ext.baseCSSPrefix + 'form-trigger-wrap',

    
    triggerNoEditCls: Ext.baseCSSPrefix + 'trigger-noedit',

    
    hideTrigger: false,

    
    editable: true,

    
    readOnly: false,

    

    
    repeatTriggerClick: false,


    
    autoSize: Ext.emptyFn,
    
    monitorTab: true,
    
    mimicing: false,
    
    triggerIndexRe: /trigger-index-(\d+)/,
    
    extraTriggerCls: '',

    componentLayout: 'triggerfield',

    initComponent: function() {
        this.wrapFocusCls = this.triggerWrapCls + '-focus';
        this.callParent(arguments);
    },

    getSubTplMarkup: function(values) {
        var me = this,
            childElCls = values.childElCls, 
            field = me.callParent(arguments);

        return [
            '<table id="', me.id, '-triggerWrap" class="', Ext.baseCSSPrefix,
                'form-trigger-wrap', childElCls,
                '" cellpadding="0" cellspacing="0" role="presentation">',
                '<tbody role="presentation">',
                    '<tr role="presentation">',
                        '<td id="', me.id, '-inputCell" class="', Ext.baseCSSPrefix,
                            'form-trigger-input-cell', childElCls, '" role="presentation">',
                            field,
                        '</td>',
                            me.getTriggerMarkup(),
                    '</tr>',
                '</tbody>',
            '</table>'
        ].join('');
    },
    
    getSubTplData: function(){
        var me = this,
            data = me.callParent(),
            readOnly = me.readOnly === true,
            editable = me.editable !== false;
        
        return Ext.apply(data, {
            editableCls: (readOnly || !editable) ? ' ' + me.triggerNoEditCls : '',
            readOnly: !editable || readOnly
        });  
    },

    getLabelableRenderData: function() {
        var me = this,
            triggerWrapCls = me.triggerWrapCls,
            result = me.callParent(arguments);

        return Ext.applyIf(result, {
            triggerWrapCls: triggerWrapCls,
            triggerMarkup: me.getTriggerMarkup()
        });
    },

    getTriggerMarkup: function() {
        var me = this,
            i = 0,
            hideTrigger = (me.readOnly || me.hideTrigger),
            triggerCls,
            triggerBaseCls = me.triggerBaseCls,
            triggerConfigs = [],
            unselectableCls = Ext.dom.Element.unselectableCls,
            style = 'width:' + me.triggerWidth + 'px;' + (hideTrigger ? 'display:none;' : ''),
            cls = me.extraTriggerCls + ' ' + Ext.baseCSSPrefix + 'trigger-cell ' + unselectableCls;

        
        
        
        
        if (!me.trigger1Cls) {
            me.trigger1Cls = me.triggerCls;
        }

        
        for (i = 0; (triggerCls = me['trigger' + (i + 1) + 'Cls']) || i < 1; i++) {
            triggerConfigs.push({
                tag: 'td',
                role: 'presentation',
                valign: 'top',
                cls: cls,
                style: style,
                cn: {
                    cls: [Ext.baseCSSPrefix + 'trigger-index-' + i, triggerBaseCls, triggerCls].join(' '),
                    role: 'presentation'
                }
            });
        }
        triggerConfigs[0].cn.cls += ' ' + triggerBaseCls + '-first';

        return Ext.DomHelper.markup(triggerConfigs);
    },
    
    disableCheck: function() {
        return !this.disabled;    
    },

    
    beforeRender: function() {
        var me = this,
            triggerBaseCls = me.triggerBaseCls,
            tempEl;
            
        
        if (!me.triggerWidth) {
            tempEl = Ext.getBody().createChild({
                role: 'presentation',
                style: 'position: absolute;', 
                cls: Ext.baseCSSPrefix + 'form-trigger'
            });
            Ext.form.field.Trigger.prototype.triggerWidth = tempEl.getWidth();
            tempEl.remove();
        }

        me.callParent();

        if (triggerBaseCls != Ext.baseCSSPrefix + 'form-trigger') {
            
            
            me.addChildEls({ name: 'triggerEl', select: '.' + triggerBaseCls });
        }

        
        me.lastTriggerStateFlags = me.getTriggerStateFlags();
    },

    onRender: function() {
        var me = this;

        me.callParent(arguments);

        me.doc = Ext.getDoc();
        me.initTrigger();
    },

    
    getTriggerWidth: function() {
        var me = this,
            totalTriggerWidth = 0;

        if (me.triggerWrap && !me.hideTrigger && !me.readOnly) {
            totalTriggerWidth = me.triggerEl.getCount() * me.triggerWidth;
        }
        return totalTriggerWidth;
    },

    setHideTrigger: function(hideTrigger) {
        if (hideTrigger != this.hideTrigger) {
            this.hideTrigger = hideTrigger;
            this.updateLayout();
        }
    },

    
    setEditable: function(editable) {
        if (editable != this.editable) {
            this.editable = editable;
            this.updateLayout();
        }
    },

    
    setReadOnly: function(readOnly) {
        var me = this,
            old = me.readOnly;
            
        me.callParent(arguments);
        if (readOnly != old) {
            me.updateLayout();
        }
    },

    
    initTrigger: function() {
        var me = this,
            triggerWrap = me.triggerWrap,
            triggerEl = me.triggerEl,
            disableCheck = me.disableCheck,
            els, len, el, i, idx, cls;

        if (me.repeatTriggerClick) {
            me.triggerRepeater = new Ext.util.ClickRepeater(triggerWrap, {
                preventDefault: true,
                handler: me.onTriggerWrapClick,
                listeners: {
                    mouseup: me.onTriggerWrapMouseup,
                    scope: me
                },
                scope: me
            });
        } else {
            me.mon(triggerWrap, {
                click: me.onTriggerWrapClick,
                mouseup: me.onTriggerWrapMouseup,
                scope: me
            });
        }

        triggerEl.setVisibilityMode(Ext.Element.DISPLAY);
        triggerEl.addClsOnOver(me.triggerBaseCls + '-over', disableCheck, me);

        els  = triggerEl.elements;
        len = els.length;

        for (i = 0; i < len; i++) {
            el = els[i];
            idx = i + 1;
            cls = me['trigger' + (idx) + 'Cls'];
            if (cls) {
                el.addClsOnOver(cls + '-over', disableCheck, me);
                el.addClsOnClick(cls + '-click', disableCheck, me);
            }
        }

        triggerEl.addClsOnClick(me.triggerBaseCls + '-click', disableCheck, me);

    },

    
    onDestroy: function() {
        var me = this;
        Ext.destroyMembers(me, 'triggerRepeater', 'triggerWrap', 'triggerEl');
        delete me.doc;
        me.callParent();
    },

    
    onFocus: function() {
        var me = this;
        me.callParent(arguments);
        if (!me.mimicing) {
            me.bodyEl.addCls(me.wrapFocusCls);
            me.mimicing = true;
            me.mon(me.doc, 'mousedown', me.mimicBlur, me, {
                delay: 10
            });
            if (me.monitorTab) {
                me.on('specialkey', me.checkTab, me);
            }
        }
    },

    
    checkTab: function(me, e) {
        if (!this.ignoreMonitorTab && e.getKey() == e.TAB) {
            this.triggerBlur();
        }
    },

    
    getTriggerStateFlags: function () {
        var me = this,
            state = 0;

        if (me.readOnly) {
            state += 1;
        }
        if (me.editable) {
            state += 2;
        }
        if (me.hideTrigger) {
            state += 4;
        }
        return state;
    },

    
    onBlur: function() {
        var me = this,
            blurTask = me.blurTask;
            
        
        if (me.blurring) {
            me.triggerBlur();
        }
    },

    
    mimicBlur: function(e) {
        if (!this.isDestroyed && !this.bodyEl.contains(e.target) && this.validateBlur(e)) {
            this.triggerBlur(e);
        }
    },

    
    triggerBlur: function(e) {
        var me = this;
        me.mimicing = false;
        me.mun(me.doc, 'mousedown', me.mimicBlur, me);
        if (me.monitorTab && me.inputEl) {
            me.un('specialkey', me.checkTab, me);
        }
        Ext.form.field.Trigger.superclass.onBlur.call(me, e);
        if (me.bodyEl) {
            me.bodyEl.removeCls(me.wrapFocusCls);
        }
    },

    
    
    validateBlur: function(e) {
        return true;
    },

    
    
    
    onTriggerWrapClick: function() {
        var me = this,
            targetEl, match,
            triggerClickMethod,
            event;

        event = arguments[me.triggerRepeater ? 1 : 0];
        if (event && !me.readOnly && !me.disabled) {
                targetEl = event.getTarget('.' + me.triggerBaseCls, null);
                match = targetEl && targetEl.className.match(me.triggerIndexRe);

            if (match) {
                triggerClickMethod = me['onTrigger' + (parseInt(match[1], 10) + 1) + 'Click'] || me.onTriggerClick;
                if (triggerClickMethod) {
                    triggerClickMethod.call(me, event);
                }
            }
        }
    },

    
    
    
    onTriggerWrapMouseup: Ext.emptyFn,

    
    onTriggerClick: Ext.emptyFn

    
    
    
});


Ext.define('Ext.form.field.Picker', {
    extend:  Ext.form.field.Trigger ,
    alias: 'widget.pickerfield',
    alternateClassName: 'Ext.form.Picker',
                                  

    
    matchFieldWidth: true,

    
    pickerAlign: 'tl-bl?',

    

    
    openCls: Ext.baseCSSPrefix + 'pickerfield-open',

    

    
    editable: true,


    initComponent: function() {
        this.callParent();

        
        this.addEvents(
            
            'expand',
            
            'collapse',
            
            'select'
        );
    },


    initEvents: function() {
        var me = this;
        me.callParent();

        
        me.keyNav = new Ext.util.KeyNav(me.inputEl, {
            down: me.onDownArrow,
            esc: {
                handler: me.onEsc,
                scope: me,
                defaultEventAction: false
            },
            scope: me,
            forceKeyDown: true
        });

        
        if (!me.editable) {
            me.mon(me.inputEl, 'click', me.onTriggerClick, me);
        }

        
        if (Ext.isGecko) {
            me.inputEl.dom.setAttribute('autocomplete', 'off');
        }
    },

    
    onEsc: function(e) {
        if (Ext.isIE) {
            
            
            
            
            e.preventDefault();
        }
        
        if (this.isExpanded) {
            this.collapse();
            e.stopEvent();
        }
    },

    onDownArrow: function(e) {
        if (!this.isExpanded) {
            
            
            this.onTriggerClick();
        }
    },

    
    expand: function() {
        var me = this,
            bodyEl, picker, collapseIf;

        if (me.rendered && !me.isExpanded && !me.isDestroyed) {
            me.expanding = true;
            bodyEl = me.bodyEl;
            picker = me.getPicker();
            collapseIf = me.collapseIf;

            
            picker.show();
            me.isExpanded = true;
            me.alignPicker();
            bodyEl.addCls(me.openCls);

            
            me.mon(Ext.getDoc(), {
                mousewheel: collapseIf,
                mousedown: collapseIf,
                scope: me
            });
            Ext.EventManager.onWindowResize(me.alignPicker, me);
            me.fireEvent('expand', me);
            me.onExpand();
            delete me.expanding;
        }
    },

    onExpand: Ext.emptyFn,

    
    alignPicker: function() {
        var me = this,
            picker = me.getPicker();

        if (me.isExpanded) {
            if (me.matchFieldWidth) {
                
                picker.setWidth(me.bodyEl.getWidth());
            }
            if (picker.isFloating()) {
                me.doAlign();
            }
        }
    },

    
    doAlign: function(){
        var me = this,
            picker = me.picker,
            aboveSfx = '-above',
            isAbove;

        
        
        me.picker.alignTo(me.triggerWrap, me.pickerAlign, me.pickerOffset);
        
        
        isAbove = picker.el.getY() < me.inputEl.getY();
        me.bodyEl[isAbove ? 'addCls' : 'removeCls'](me.openCls + aboveSfx);
        picker[isAbove ? 'addCls' : 'removeCls'](picker.baseCls + aboveSfx);
    },

    
    collapse: function() {
        if (this.isExpanded && !this.isDestroyed) {
            var me = this,
                openCls = me.openCls,
                picker = me.picker,
                doc = Ext.getDoc(),
                collapseIf = me.collapseIf,
                aboveSfx = '-above';

            
            picker.hide();
            me.isExpanded = false;

            
            me.bodyEl.removeCls([openCls, openCls + aboveSfx]);
            picker.el.removeCls(picker.baseCls + aboveSfx);

            
            doc.un('mousewheel', collapseIf, me);
            doc.un('mousedown', collapseIf, me);
            Ext.EventManager.removeResizeListener(me.alignPicker, me);
            me.fireEvent('collapse', me);
            me.onCollapse();
        }
    },

    onCollapse: Ext.emptyFn,


    
    collapseIf: function(e) {
        var me = this;

        if (!me.isDestroyed && !e.within(me.bodyEl, false, true) && !e.within(me.picker.el, false, true) && !me.isEventWithinPickerLoadMask(e)) {
            me.collapse();
        }
    },

    
    getPicker: function() {
        var me = this;
        return me.picker || (me.picker = me.createPicker());
    },

    
    createPicker: Ext.emptyFn,

    
    onTriggerClick: function() {
        var me = this;
        if (!me.readOnly && !me.disabled) {
            if (me.isExpanded) {
                me.collapse();
            } else {
                me.expand();
            }
            me.inputEl.focus();
        }
    },
    
    triggerBlur: function() {
        var picker = this.picker;
            
        this.callParent(arguments);
        if (picker && picker.isVisible()) {
            picker.hide();
        }
    },

    mimicBlur: function(e) {
        var me = this,
            picker = me.picker;
        
        if (!picker || !e.within(picker.el, false, true) && !me.isEventWithinPickerLoadMask(e)) {
            me.callParent(arguments);
        }
    },

    onDestroy : function(){
        var me = this,
            picker = me.picker;

        Ext.EventManager.removeResizeListener(me.alignPicker, me);
        Ext.destroy(me.keyNav);
        if (picker) {
            delete picker.pickerField;
            picker.destroy();
        }
        me.callParent();
    },

    
    isEventWithinPickerLoadMask: function(e) {
        var loadMask = this.picker.loadMask;

        return loadMask ? e.within(loadMask.maskEl, false, true) || e.within(loadMask.el, false, true) : false;
    }

});



Ext.define('Ext.selection.Model', {
    extend:  Ext.util.Observable ,
    alternateClassName: 'Ext.AbstractSelectionModel',
                                        
    mixins: {
        bindable:  Ext.util.Bindable     
    },
    

    

    
    allowDeselect: undefined,
    
    
    toggleOnClick: true,

    
    selected: null,

    
    pruneRemoved: true,
    
    suspendChange: 0,

    constructor: function(cfg) {
        var me = this;

        cfg = cfg || {};
        Ext.apply(me, cfg);

        me.addEvents(
            
            'selectionchange',
            
            'focuschange'
        );

        me.modes = {
            SINGLE: true,
            SIMPLE: true,
            MULTI: true
        };

        
        me.setSelectionMode(cfg.mode || me.mode);

        
        me.selected = new Ext.util.MixedCollection(null, me.getSelectionId);

        me.callParent(arguments);
    },

    
    bindStore: function(store, initial){
        var me = this;
        me.mixins.bindable.bindStore.apply(me, arguments);
        if(me.store && !initial) {
            me.refresh();
        }
    },

    getStoreListeners: function() {
        var me = this;
        return {
            add: me.onStoreAdd,
            clear: me.onStoreClear,
            bulkremove: me.onStoreRemove,
            update: me.onStoreUpdate,
            load: me.onStoreLoad,
            idchanged: me.onModelIdChanged,
            refresh: me.onStoreRefresh
        };
    },
    
    suspendChanges: function(){
        ++this.suspendChange;
    },
    
    resumeChanges: function(){
        if (this.suspendChange) {
            --this.suspendChange;
        }
    },

    
    selectAll: function(suppressEvent) {
        var me = this,
            selections = me.store.getRange(),
            i = 0,
            len = selections.length,
            start = me.getSelection().length;

        me.suspendChanges();
        for (; i < len; i++) {
            me.doSelect(selections[i], true, suppressEvent);
        }
        me.resumeChanges();
        
        if (!suppressEvent) {
            me.maybeFireSelectionChange(me.getSelection().length !== start);
        }
    },

    
    deselectAll: function(suppressEvent) {
        var me = this,
            selections = me.getSelection(),
            selIndexes = {},
            store = me.store,
            start = selections.length,
            i, l, rec;

        
        
        
        
        
        
        for (i = 0, l = selections.length; i < l; i++) {
            rec = selections[i];
            
            selIndexes[rec.internalId] = store.indexOf(rec);
        }
        
        
        
        selections = Ext.Array.sort(selections, function(r1, r2){
            var idx1 = selIndexes[r1.internalId],
                idx2 = selIndexes[r2.internalId];
            
            
            return idx1 < idx2 ? -1 : 1;
        });
        
        me.suspendChanges();
        me.doDeselect(selections, suppressEvent);
        me.resumeChanges();
        
        if (!suppressEvent) {
            me.maybeFireSelectionChange(me.getSelection().length !== start);
        }
    },

    
    
    
    selectWithEvent: function(record, e) {
        var me = this,
            isSelected = me.isSelected(record),
            shift = e.shiftKey,
            ctrl = e.ctrlKey,
            start = me.selectionStart,
            selected = me.getSelection(),
            len = selected.length,
            allowDeselect = me.allowDeselect,
            toDeselect, i, item;

        switch (me.selectionMode) {
            case 'MULTI':
                if (shift && start) {
                    me.selectRange(start, record, ctrl);
                } else if (ctrl && isSelected) {
                    me.doDeselect(record, false);
                } else if (ctrl) {
                    me.doSelect(record, true, false);
                } else if (isSelected && !shift && !ctrl && len > 1) {
                    toDeselect = [];
                    
                    for (i = 0; i < len; ++i) {
                        item = selected[i];
                        if (item !== record) {
                            toDeselect.push(item);    
                        }
                    }
                    
                    me.doDeselect(toDeselect);
                } else if (!isSelected) {
                    me.doSelect(record, false);
                }
                break;
            case 'SIMPLE':
                if (isSelected) {
                    me.doDeselect(record);
                } else {
                    me.doSelect(record, true);
                }
                break;
            case 'SINGLE':
                if (allowDeselect && !ctrl) {
                    allowDeselect = me.toggleOnClick;
                }
                if (allowDeselect && isSelected) {
                    me.doDeselect(record);
                } else {
                    me.doSelect(record, false);
                }
                break;
        }

        
        
        
        
        if (!shift) {
            if (me.isSelected(record)) {
                me.selectionStart = record;
            } else {
                me.selectionStart = null;
            }
        }
    },

    
    
    
    afterKeyNavigate: function(e, record) {
        var me = this,
            recIdx,
            fromIdx,
            isSelected = me.isSelected(record),
            from = (me.selectionStart && me.isSelected(me.lastFocused)) ? me.selectionStart : (me.selectionStart = me.lastFocused),
            key = e.getCharCode(),
            isSpace = key === e.SPACE,
            direction = key === e.UP || key === e.PAGE_UP ? 'up' : (key === e.DOWN || key === e.DOWN ? 'down' : null);

        switch (me.selectionMode) {
            case 'MULTI':

                if (isSpace) {
                    
                    if (e.shiftKey) {
                        me.selectRange(from, record, e.ctrlKey);
                    } else {
                        
                        
                        if (isSelected) {
                            me.doDeselect(record, e.ctrlKey);

                            
                            
                            me.setLastFocused(null);
                            me.setLastFocused(record);
                        }
                        
                        else {
                            me.doSelect(record, e.ctrlKey);
                        }
                    }
                }

                
                else if (e.shiftKey && from) {

                    
                    fromIdx = me.store.indexOf(from);
                    recIdx = me.store.indexOf(record);

                    
                    if (direction === 'up' && fromIdx <= recIdx) {
                        me.deselectRange(me.lastFocused, recIdx + 1);
                    }
                    else if (direction === 'down' && fromIdx >= recIdx) {
                        me.deselectRange(me.lastFocused, recIdx - 1);
                    }

                    
                    else if (from !== record) {
                        me.selectRange(from, record, e.ctrlKey);
                    }
                    me.lastSelected = record;
                    me.setLastFocused(record);
                }

                
                else if (e.ctrlKey && isSelected) {
                    me.setLastFocused(record);
                }

                
                else if (e.ctrlKey) {
                    me.setLastFocused(record);
                }

                
                else {
                    me.doSelect(record, false);
                }
                break;
            case 'SIMPLE':
                if (isSelected) {
                    me.doDeselect(record);
                } else {
                    me.doSelect(record, true);
                }
                break;
            case 'SINGLE':
                
                if (isSpace) {
                    if (isSelected) {
                        me.doDeselect(record);
                        me.setLastFocused(record);
                    } else {
                        me.doSelect(record);
                    }
                }

                
                else if (e.ctrlKey) {
                    me.setLastFocused(record);
                }

                
                else if (me.allowDeselect && isSelected) {
                    me.doDeselect(record);
                }

                
                else {
                    me.doSelect(record, false);
                }
                break;
        }

        
        
        
        
        if (!e.shiftKey) {
            if (me.isSelected(record)) {
                me.selectionStart = record;
            }
        }
    },

    
    selectRange : function(startRow, endRow, keepExisting) {
        var me = this,
            store = me.store,
            selected = me.selected.items,
            result, i, len, toSelect, toDeselect, idx, rec;

        if (me.isLocked()){
            return;
        }

        result = me.normalizeRowRange(startRow, endRow);
        startRow = result[0];
        endRow = result[1];

        toSelect = [];
        for (i = startRow; i <= endRow; i++){
            if (!me.isSelected(store.getAt(i))) {
                toSelect.push(store.getAt(i));
            }
        }
        
        if (!keepExisting) {
            
            toDeselect = [];
            me.suspendChanges();
            
            for (i = 0, len = selected.length; i < len; ++i) {
                rec = selected[i];
                idx = store.indexOf(rec);
                if (idx < startRow || idx > endRow) {
                    toDeselect.push(rec)
                }
            }
            
            for (i = 0, len = toDeselect.length; i < len; ++i) {
                me.doDeselect(toDeselect[i]);
            }
            me.resumeChanges();
        }
        
        me.doMultiSelect(toSelect, true);
    },

    
    deselectRange : function(startRow, endRow) {
        var me = this,
            store = me.store,
            result, i, toDeselect, record;

        if (me.isLocked()){
            return;
        }

        result = me.normalizeRowRange(startRow, endRow);
        startRow = result[0];
        endRow = result[1];

        toDeselect = [];
        for (i = startRow; i <= endRow; i++) {
            record = store.getAt(i);
            if (me.isSelected(record)) {
                toDeselect.push(record);
            }
        }
        me.doDeselect(toDeselect);
    },
    
    normalizeRowRange: function(startRow, endRow) {
        var store = this.store,
            tmp;
        
        if (!Ext.isNumber(startRow)) {
            startRow = store.indexOf(startRow);
        }
        startRow = Math.max(0, startRow);
        
        if (!Ext.isNumber(endRow)) {
            endRow = store.indexOf(endRow);
        }
        endRow = Math.min(endRow, store.getCount() - 1);
        
        
        if (startRow > endRow){
            tmp = endRow;
            endRow = startRow;
            startRow = tmp;
        }    
        
        return [startRow, endRow];
    },

    onModelIdChanged: function(store, model, oldId, newId, oldInternalId) {
        this.selected.updateKey(oldInternalId, newId);
    },

    
    select: function(records, keepExisting, suppressEvent) {
        
        if (Ext.isDefined(records)) {
            this.doSelect(records, keepExisting, suppressEvent);
        }
    },

    
    deselect: function(records, suppressEvent) {
        this.doDeselect(records, suppressEvent);
    },

    doSelect: function(records, keepExisting, suppressEvent) {
        var me = this,
            record;

        if (me.locked || !me.store) {
            return;
        }
        if (typeof records === "number") {
            record = me.store.getAt(records);
            
            if (!record) {
                return;
            }
            records = [record];
        }
        if (me.selectionMode == "SINGLE" && records) {
            record = records.length ? records[0] : records;
            me.doSingleSelect(record, suppressEvent);
        } else {
            me.doMultiSelect(records, keepExisting, suppressEvent);
        }
    },

    doMultiSelect: function(records, keepExisting, suppressEvent) {
        var me = this,
            selected = me.selected,
            change = false,
            result, i, len, record, commit;

        if (me.locked) {
            return;
        }

        records = !Ext.isArray(records) ? [records] : records;
        len = records.length;
        if (!keepExisting && selected.getCount() > 0) {
            result = me.deselectDuringSelect(records, selected.getRange(), suppressEvent);
            if (result[0]) {
                
                
                me.maybeFireSelectionChange(result[1] > 0 && !suppressEvent);
                return;
            }
        }

        commit = function() {
            selected.add(record);
            change = true;
        };

        for (i = 0; i < len; i++) {
            record = records[i];
            if (me.isSelected(record)) {
                continue;
            }
            me.lastSelected = record;

            me.onSelectChange(record, true, suppressEvent, commit);
        }
        if (!me.preventFocus) {
            me.setLastFocused(record, suppressEvent);
        }
        
        me.maybeFireSelectionChange(change && !suppressEvent);
    },
    
    deselectDuringSelect: function(toSelect, selected, suppressEvent) {
        var me = this,
            len = selected.length,
            changed = 0,
            failed = false,
            item, i;
            
        
        me.suspendChanges();
        for (i = 0; i < len; ++i) {
            item = selected[i];
            if (!Ext.Array.contains(toSelect, item)) {
                if (me.doDeselect(item, suppressEvent)) {
                    ++changed;
                } else {
                    failed = true;
                }
            }
        }
        me.resumeChanges();
        
        return [failed, changed];
    },

    
    doDeselect: function(records, suppressEvent) {
        var me = this,
            selected = me.selected,
            i = 0,
            len, record,
            attempted = 0,
            accepted = 0,
            commit;

        if (me.locked || !me.store) {
            return false;
        }

        if (typeof records === "number") {
            
            record = me.store.getAt(records);
            if (!record) {
                return false;
            }
            records = [record];
        } else if (!Ext.isArray(records)) {
            records = [records];
        }

        commit = function() {
            ++accepted;
            selected.remove(record);
        };

        len = records.length;

        me.suspendChanges();
        for (; i < len; i++) {
            record = records[i];
            if (me.isSelected(record)) {
                if (me.lastSelected === record) {
                    me.lastSelected = selected.last();
                    if (me.lastFocused === record) {
                        me.setLastFocused(null);
                    }
                }
                ++attempted;
                me.onSelectChange(record, false, suppressEvent, commit);
            }
        }
        me.resumeChanges();

        
        me.maybeFireSelectionChange(accepted > 0 && !suppressEvent);
        return accepted === attempted;
    },

    doSingleSelect: function(record, suppressEvent) {
        var me = this,
            changed = false,
            selected = me.selected,
            commit;

        if (me.locked) {
            return;
        }
        
        
        if (me.isSelected(record)) {
            return;
        }
        
        if (selected.getCount()) {
            me.suspendChanges();
            if (!me.doDeselect(me.lastSelected, suppressEvent)) {
                me.resumeChanges();
                return;
            }
            me.resumeChanges();
        }

        commit = function() {
            selected.add(record);
            me.lastSelected = record;
            changed = true;
        };

        me.onSelectChange(record, true, suppressEvent, commit);

        if (changed) {
            if (!suppressEvent && !me.preventFocus) {
                me.setLastFocused(record);
            }
            me.maybeFireSelectionChange(!suppressEvent);
        }
    },

    
    setLastFocused: function(record, supressFocus) {
        var me = this,
            recordBeforeLast = me.lastFocused;

        
        if (record !== recordBeforeLast) {
            me.lastFocused = record;
            me.onLastFocusChanged(recordBeforeLast, record, supressFocus);
        }
    },

    
    isFocused: function(record) {
        return record === this.getLastFocused();
    },

    
    
    maybeFireSelectionChange: function(fireEvent) {
        var me = this;
        if (fireEvent && !me.suspendChange) {
            me.fireEvent('selectionchange', me, me.getSelection());
        }
    },

    
    getLastSelected: function() {
        return this.lastSelected;
    },

    getLastFocused: function() {
        return this.lastFocused;
    },

    
    getSelection: function() {
        return this.selected.getRange();
    },

    
    getSelectionMode: function() {
        return this.selectionMode;
    },

    
    setSelectionMode: function(selMode) {
        selMode = selMode ? selMode.toUpperCase() : 'SINGLE';
        
        
        this.selectionMode = this.modes[selMode] ? selMode : 'SINGLE';
    },

    
    isLocked: function() {
        return this.locked;
    },

    
    setLocked: function(locked) {
        this.locked = !!locked;
    },

    
    isRangeSelected: function(startRow, endRow) {
        var me = this,
            store = me.store,
            i, result;

        result = me.normalizeRowRange(startRow, endRow);
        startRow = result[0];
        endRow = result[1];

        
        for (i = startRow; i <= endRow; i++) {
            if (!me.isSelected(store.getAt(i))) {
                return false;
            }
        }
        return true;
    },

    
    isSelected: function(record) {
        record = Ext.isNumber(record) ? this.store.getAt(record) : record;
        return this.selected.contains(record);
    },

    
    hasSelection: function() {
        return this.selected.getCount() > 0;
    },

    getSelectionId: function(record){
        return record.internalId;
    },

    pruneIf: function() {
        var me = this,
            selected = me.selected,
            toRemove = [],
            len = selected.length,
            i, item;

        if (me.pruneRemoved) {
            for (i = 0; i < len; i++) {
                item = selected.getAt(i);
                if (!this.storeHasSelected(item)) {
                    toRemove.push(item);
                }
            }
            if (toRemove.length) {
                for (i = 0, len = toRemove.length; i < len; i++) {
                    selected.remove(toRemove[i]);
                }
                me.maybeFireSelectionChange(true);
            }
        }
    },

    
    
    
    storeHasSelected: function(record) {
        var store = this.store,
            records,
            len, id, i;

        if (record.hasId() && store.getById(record.getId())) {
            return true;
        } else {
            records = store.data.items;
            len = records.length;
            id = record.internalId;

            for (i = 0; i < len; ++i) {
                if (id === records[i].internalId) {
                    return true;
                }
            }
        }
        return false;
    },

    refresh: function() {
        var me = this,
            store = me.store,
            rec,
            toBeSelected = [],
            toBeReAdded = [],
            oldSelections = me.getSelection(),
            len = oldSelections.length,
            selection,
            change,
            i = 0,
            lastFocused = me.getLastFocused();

        
        if (!store) {
            return;
        }

        
        
        for (; i < len; i++) {
            selection = oldSelections[i];
            if (store.indexOf(selection) !== -1) {
                toBeSelected.push(selection);
            }

            
            else if (!me.pruneRemoved) {
                
                rec = store.getById(selection.getId());
                if (rec) {
                    toBeSelected.push(rec);
                }
                
                else {
                    toBeReAdded.push(selection)
                }
            }

            
            if (me.mode === 'SINGLE' && toBeReAdded.length) {
                break;
            }
        }

        
        
        if (me.selected.getCount() != (toBeSelected.length + toBeReAdded.length)) {
            change = true;
        }

        me.clearSelections();

        if (store.indexOf(lastFocused) !== -1) {
            
            me.setLastFocused(lastFocused, true);
        }

        if (toBeSelected.length) {
            
            me.doSelect(toBeSelected, false, true);
        }

        
        if (toBeReAdded.length) {
            me.selected.addAll(toBeReAdded);

            
            if (!me.lastSelected) {
                me.lastSelected = toBeReAdded[toBeReAdded.length - 1];
            }
        }

        me.maybeFireSelectionChange(change);
    },

    
    clearSelections: function() {
        
        this.selected.clear();
        this.lastSelected = null;
        this.setLastFocused(null);
    },

    
    onStoreAdd: Ext.emptyFn,

    
    
    onStoreClear: function() {
        if (this.selected.getCount() > 0) {
            this.clearSelections();
            this.maybeFireSelectionChange(true);
        }
    },

    
    
    
    onStoreRemove: function(store, records, indexes, isMove) {
        var me = this;

        
        if (me.selectionStart && Ext.Array.contains(records, me.selectionStart)) {
            me.selectionStart = null;
        }

        if (isMove || me.locked || !me.pruneRemoved) {
            return;
        }
        me.deselectDeletedRecords(records);
    },

    
    
    deselectDeletedRecords: function(records) {
        var me = this,
            selected = me.selected,
            i, length = records.length,
            removed = 0,
            record;

        
        for (i = 0; i < length; i++) {
            record = records[i];
            if (selected.remove(record)) {
                if (me.lastSelected == record) {
                    me.lastSelected = null;
                }
                if (me.getLastFocused() == record) {
                    me.setLastFocused(null);
                }
                ++removed;
            }
        }
        if (removed) {
            me.maybeFireSelectionChange(true);
        }
    },

    
    getCount: function() {
        return this.selected.getCount();
    },

    
    onUpdate: Ext.emptyFn,

    
    destroy: function(){
        this.clearListeners();    
    },

    
    onStoreUpdate: Ext.emptyFn,

    onStoreRefresh: Ext.emptyFn,

    
    onStoreLoad: Ext.emptyFn,

    
    onSelectChange: function(record, isSelected, suppressEvent, commitFn) {
        var me = this,
            eventName = isSelected ? 'select' : 'deselect';

        if ((suppressEvent || me.fireEvent('before' + eventName, me, record)) !== false &&
           commitFn() !== false) {

            if (!suppressEvent) {
                me.fireEvent(eventName, me, record);
            }
        }   
    },

    
    onLastFocusChanged: function(oldFocused, newFocused) {
        this.fireEvent('focuschange', this, oldFocused, newFocused);
    },

    
    onEditorKey: Ext.emptyFn,

    
    beforeViewRender: function(view) {
        this.views = this.views || [];
        this.views.push(view);
        this.bindStore(view.getStore(), true);
    },

    
    bindComponent: Ext.emptyFn
});


Ext.define('Ext.selection.DataViewModel', {
    extend:  Ext.selection.Model ,

                                  

    deselectOnContainerClick: true,

    
    enableKeyNav: true,

    constructor: function(cfg){
        this.addEvents(
            
            'beforedeselect',

            
            'beforeselect',

            
            'deselect',

            
            'select'
        );
        this.callParent(arguments);
    },

    bindComponent: function(view) {
        var me = this,
            eventListeners = {
                refresh: me.refresh,
                scope: me
            };

        me.view = view;
        me.bindStore(view.getStore());

        eventListeners[view.triggerEvent] = me.onItemClick;
        eventListeners[view.triggerCtEvent] = me.onContainerClick;

        view.on(eventListeners);

        if (me.enableKeyNav) {
            me.initKeyNav(view);
        }
    },
    
    onUpdate: function(record){
        var view = this.view;
        if (view && this.isSelected(record)) {
            view.onItemSelect(record);
        }
    },

    onItemClick: function(view, record, item, index, e) {
        this.selectWithEvent(record, e);
    },

    onContainerClick: function() {
        if (this.deselectOnContainerClick) {
            this.deselectAll();
        }
    },

    initKeyNav: function(view) {
        var me = this;

        if (!view.rendered) {
            view.on({
                render: Ext.Function.bind(me.initKeyNav, me, [view]),
                single: true
            });
            return;
        }

        view.el.set({
            tabIndex: -1
        });
        me.keyNav = new Ext.util.KeyNav({
            target: view.el,
            ignoreInputFields: true,
            down: Ext.pass(me.onNavKey, [1], me),
            right: Ext.pass(me.onNavKey, [1], me),
            left: Ext.pass(me.onNavKey, [-1], me),
            up: Ext.pass(me.onNavKey, [-1], me),
            scope: me
        });
    },

    onNavKey: function(step) {
        step = step || 1;
        var me = this,
            view = me.view,
            selected = me.getSelection()[0],
            numRecords = me.view.store.getCount(),
            idx;

        if (selected) {
            idx = view.indexOf(view.getNode(selected)) + step;
        } else {
            idx = 0;
        }

        if (idx < 0) {
            idx = numRecords - 1;
        } else if (idx >= numRecords) {
            idx = 0;
        }

        me.select(idx);
    },

    
    onSelectChange: function(record, isSelected, suppressEvent, commitFn) {
        var me = this,
            view = me.view,
            eventName = isSelected ? 'select' : 'deselect';

        if ((suppressEvent || me.fireEvent('before' + eventName, me, record)) !== false &&
                commitFn() !== false) {

            if (view) {
                if (isSelected) {
                    view.onItemSelect(record);
                } else {
                    view.onItemDeselect(record);
                }
            }

            if (!suppressEvent) {
                me.fireEvent(eventName, me, record);
            }
        }
    },
    
    onLastFocusChanged: function(oldFocus, newFocus, suppressFocus){
        var view = this.view;
        if (view && !suppressFocus && newFocus) {
            view.focusNode(newFocus);
            this.fireEvent('focuschange', this, oldFocus, newFocus);
        }
    },
    
    destroy: function(){
        Ext.destroy(this.keyNav);
        this.callParent();
    }
});


Ext.define('Ext.view.AbstractView', {
    extend:  Ext.Component ,
               
                       
                                
                                   
                       
                                     
      
    mixins: {
        bindable:  Ext.util.Bindable 
    },

    inheritableStatics: {
        getRecord: function(node) {
            return this.getBoundView(node).getRecord(node);
        },

        getBoundView: function(node) {
            return Ext.getCmp(node.boundView);
        }
    },

    
    

    
    deferInitialRefresh: true,

    

    
    itemCls: Ext.baseCSSPrefix + 'dataview-item',

    

    

    
    
    loadingText: 'Loading...',
    

    
    loadMask: true,

    

    
    loadingUseMsg: true,


    

    
    selectedItemCls: Ext.baseCSSPrefix + 'item-selected',

    
    
    emptyText: "",
    

    
    deferEmptyText: true,

    
    trackOver: false,

    
    blockRefresh: false,

    

    
    preserveScrollOnRefresh: false,

    ariaRole: 'listbox',
    itemAriaRole: 'option',
    
    
    last: false,

    triggerEvent: 'itemclick',
    triggerCtEvent: 'containerclick',

    addCmpEvents: function() {

    },

    
    initComponent : function(){
        var me = this,
            isDef = Ext.isDefined,
            itemTpl = me.itemTpl,
            memberFn = {};

        if (itemTpl) {
            if (Ext.isArray(itemTpl)) {
                
                itemTpl = itemTpl.join('');
            } else if (Ext.isObject(itemTpl)) {
                
                memberFn = Ext.apply(memberFn, itemTpl.initialConfig);
                itemTpl = itemTpl.html;
            }

            if (!me.itemSelector) {
                me.itemSelector = '.' + me.itemCls;
            }

            itemTpl = Ext.String.format('<tpl for="."><div class="{0}" role="{2}">{1}</div></tpl>', me.itemCls, itemTpl, me.itemAriaRole);
            me.tpl = new Ext.XTemplate(itemTpl, memberFn);
        }


        me.callParent();
        me.tpl = me.getTpl('tpl');

        
        if (me.overItemCls) {
            me.trackOver = true;
        }

        me.addEvents(
            
            'beforerefresh',
            
            'refresh',
            
            'viewready',
            
            'itemupdate',
            
            'itemadd',
            
            'itemremove'
        );

        me.addCmpEvents();

        
        me.store = Ext.data.StoreManager.lookup(me.store || 'ext-empty-store');

        
        if (!me.dataSource) {
            me.dataSource = me.store;
        }
        
        
        me.bindStore(me.dataSource, true, 'dataSource');
        if (!me.all) {
            me.all = new Ext.CompositeElementLite();
        }

        
        me.scrollState = {
            top: 0,
            left: 0
        };
        me.on({
            scroll: me.onViewScroll,
            element: 'el',
            scope: me
        });
    },

    onRender: function() {
        var me = this,
            mask = me.loadMask,
            maskStore = me.getMaskStore(),
            cfg = {
                target: me,
                msg: me.loadingText,
                msgCls: me.loadingCls,
                useMsg: me.loadingUseMsg,
                
                
                store: maskStore
            };

        me.callParent(arguments);

        if (mask && !maskStore.proxy.isSynchronous) {
            
            if (Ext.isObject(mask)) {
                cfg = Ext.apply(cfg, mask);
            }
            
            
            
            
            me.loadMask = new Ext.LoadMask(cfg);
            me.loadMask.on({
                scope: me,
                beforeshow: me.onMaskBeforeShow,
                hide: me.onMaskHide
            });
        }
    },
    
    finishRender: function() {
        var me = this;
        me.callParent(arguments);
        
        
        if (!me.up('[collapsed],[hidden]')) {
            me.doFirstRefresh(me.dataSource);
        }
    },

    onBoxReady: function() {
        var me = this;

        me.callParent(arguments);

        
        
        if (!me.firstRefreshDone) {
            me.doFirstRefresh(me.dataSource);
        }
    },

    getMaskStore: function(){
        return this.store;    
    },

    onMaskBeforeShow: function(){
        var me = this,
            loadingHeight = me.loadingHeight;

        if (loadingHeight && loadingHeight > me.getHeight()) {
            me.hasLoadingHeight = true;
            me.oldMinHeight = me.minHeight;
            me.minHeight = loadingHeight;
            me.updateLayout();
        }
    },

    onMaskHide: function(){
        var me = this;

        if (!me.destroying && me.hasLoadingHeight) {
            me.minHeight = me.oldMinHeight;
            me.updateLayout();
            delete me.hasLoadingHeight;
        }
    },

    beforeRender: function() {
        this.callParent(arguments);
        this.getSelectionModel().beforeViewRender(this);
    },

    afterRender: function() {
        this.callParent(arguments);

        
        
        
        this.getSelectionModel().bindComponent(this);
    },

    
    getSelectionModel: function(){
        var me = this,
            mode = 'SINGLE';

        if (me.simpleSelect) {
            mode = 'SIMPLE';
        } else if (me.multiSelect) {
            mode = 'MULTI';
        }

        
        if (!me.selModel || !me.selModel.events) {
            me.selModel = new Ext.selection.DataViewModel(Ext.apply({
                allowDeselect: me.allowDeselect,
                mode: mode
            }, me.selModel));
        }

        if (!me.selModel.hasRelaySetup) {
            me.relayEvents(me.selModel, [
                'selectionchange', 'beforeselect', 'beforedeselect', 'select', 'deselect', 'focuschange'
            ]);
            me.selModel.hasRelaySetup = true;
        }

        
        
        if (me.disableSelection) {
            me.selModel.locked = true;
        }

        return me.selModel;
    },

    
    refresh: function() {
        var me = this,
            targetEl,
            targetParent,
            oldDisplay,
            nextSibling,
            dom,
            records;

        if (!me.rendered || me.isDestroyed) {
            return;
        }

        if (!me.hasListeners.beforerefresh || me.fireEvent('beforerefresh', me) !== false) {
            targetEl = me.getTargetEl();
            records = me.getViewRange();
            dom = targetEl.dom;

            
            
            if (!me.preserveScrollOnRefresh) {
                targetParent = dom.parentNode;
                oldDisplay = dom.style.display;
                dom.style.display = 'none';
                nextSibling = dom.nextSibling;
                targetParent.removeChild(dom);
            }

            if (me.refreshCounter) {
                me.clearViewEl();
            } else {
                me.fixedNodes = targetEl.dom.childNodes.length;
                me.refreshCounter = 1;
            }

            
            
            
            
            me.tpl.append(targetEl, me.collectData(records, me.all.startIndex));

            
            
            if (records.length < 1) {
                
                if (!this.store.loading && (!me.deferEmptyText || me.hasFirstRefresh)) {
                    Ext.core.DomHelper.insertHtml('beforeEnd', targetEl.dom, me.emptyText);
                }
                me.all.clear();
            } else {
                me.collectNodes(targetEl.dom);
                me.updateIndexes(0);
            }

            
            if (me.hasFirstRefresh) {
                
                if (me.refreshSelmodelOnRefresh !== false) {
                    me.selModel.refresh();
                } else {
                    
                    me.selModel.pruneIf();
                }
            }

            me.hasFirstRefresh = true;

            if (!me.preserveScrollOnRefresh) {
                targetParent.insertBefore(dom, nextSibling);
                dom.style.display = oldDisplay;
            }

            
            this.refreshSize();

            me.fireEvent('refresh', me);

            
            
            if (!me.viewReady) {
                
                
                me.viewReady = true;
                me.fireEvent('viewready', me);
            }
        }
    },

    
    
    collectNodes: function(targetEl) {
        this.all.fill(Ext.query(this.getItemSelector(), Ext.getDom(targetEl)), this.all.startIndex);
    },

    getViewRange: function() {
        return this.dataSource.getRange();
    },

    
    refreshSize: function() {
        var sizeModel = this.getSizeModel();
        if (sizeModel.height.shrinkWrap || sizeModel.width.shrinkWrap) {
            this.updateLayout();
        }
    },

    clearViewEl: function(){
        
        
        
        
        
        
        

        var me = this,
            el = me.getTargetEl();

        if (me.fixedNodes) {
            while (el.dom.childNodes[me.fixedNodes]) {
                el.dom.removeChild(el.dom.childNodes[me.fixedNodes]);
            }
        } else {
            el.update('');
        }
        me.refreshCounter++;
    },

    
    onViewScroll: Ext.emptyFn,
    
    onIdChanged: Ext.emptyFn,

    
    saveScrollState: function() {
        if (this.rendered) {
            var dom = this.el.dom,
                state = this.scrollState;

            state.left = dom.scrollLeft;
            state.top = dom.scrollTop;
        }
    },

    
    restoreScrollState: function() {
        if (this.rendered) {
            var dom = this.el.dom, 
                state = this.scrollState;

            dom.scrollLeft = state.left;
            dom.scrollTop = state.top;
        }
    },

    
    prepareData: function(data, index, record) {
        var associatedData, attr, hasCopied;
        if (record) {
            associatedData = record.getAssociatedData();
            for (attr in associatedData) {
                if (associatedData.hasOwnProperty(attr)) {
                    
                    
                    
                    
                    if (!hasCopied) {
                        data = Ext.Object.chain(data);
                        hasCopied = true;
                    }
                    data[attr] = associatedData[attr];
                }
            }
        }
        return data;
    },

    
    collectData: function(records, startIndex){
        var data = [],
            i = 0,
            len = records.length,
            record;

        for (; i < len; i++) {
            record = records[i];
            data[i] = this.prepareData(record.data, startIndex + i, record);
        }
        return data;
    },

    
    bufferRender : function(records, index) {
        var me = this,
            div = me.renderBuffer || (me.renderBuffer = document.createElement('div'));

        me.tpl.overwrite(div, me.collectData(records, index));
        return  Ext.DomQuery.select(me.getItemSelector(), div);
    },
    
    getNodeContainer: function() {
        return this.getTargetEl();
    },

    
    onUpdate : function(ds, record){
        var me = this,
            index,
            node;

        if (me.viewReady) {
            index = me.dataSource.indexOf(record);
            if (index > -1) {
                node = me.bufferRender([record], index)[0];
                
                if (me.getNode(record)) {
                    me.all.replaceElement(index, node, true);
                    me.updateIndexes(index, index);
                    
                    me.selModel.onUpdate(record);
                    if (me.hasListeners.itemupdate) {
                        me.fireEvent('itemupdate', record, index, node);
                    }
                    return node;
                }
            }
        }

    },

    
    onAdd : function(store, records, index) {
        var me = this,
            nodes;

        if (me.rendered) {
            
            
            if (me.all.getCount() === 0) {
                me.refresh();
                nodes = me.all.slice();
            } else {
                nodes = me.doAdd(records, index);
                
                if (me.refreshSelmodelOnRefresh !== false) {
                    me.selModel.refresh();
                }
                me.updateIndexes(index);

                
                me.refreshSize();
            }

            if (me.hasListeners.itemadd) {
                me.fireEvent('itemadd', records, index, nodes);
            }
        }

    },

    doAdd: function(records, index) {
        var me = this,
            nodes = me.bufferRender(records, index, true),
            all = me.all,
            count = all.getCount(),
            i, l, nodeContainer, fragment;

        if (count === 0) {
            nodeContainer = this.getNodeContainer();
            fragment = document.createDocumentFragment();

            for (i = 0, l = nodes.length; i < l; i++) {
                fragment.appendChild(nodes[i]);
            }
            nodeContainer.appendChild(fragment);
        } else if (index < count) {
            if (index === 0) {
                all.item(index).insertSibling(nodes, 'before', true);
            } else {
                all.item(index - 1).insertSibling(nodes, 'after', true);
            }
        } else {
            all.last().insertSibling(nodes, 'after', true);
        }

        all.insert(index, nodes);
        return nodes;
    },

    
    onRemove : function(ds, records, indexes) {
        var me = this,
            fireItemRemove = me.hasListeners.itemremove,
            i,
            record,
            index;

        if (me.all.getCount()) {
            if (me.dataSource.getCount() === 0) {
                
                if (fireItemRemove) {
                    for (i = indexes.length - 1; i >= 0; --i) {
                        me.fireEvent('itemremove', records[i], indexes[i]);
                    }
                }
                me.refresh();
            } else {
                
                
                for (i = indexes.length - 1; i >= 0; --i) {
                    record = records[i];
                    index = indexes[i];
                    me.doRemove(record, index);
                    if (fireItemRemove) {
                        me.fireEvent('itemremove', record, index);
                    }
                }
                me.updateIndexes(indexes[0]);
            }

            
            this.refreshSize();
        }
    },

    
    doRemove: function(record, index) {
        this.all.removeElement(index, true);
    },

    
    refreshNode : function(index) {
        this.onUpdate(this.dataSource, this.dataSource.getAt(index));
    },

    
    updateIndexes : function(startIndex, endIndex) {
        var nodes = this.all.elements,
            records = this.getViewRange(),
            i;

        startIndex = startIndex || 0;
        endIndex = endIndex || ((endIndex === 0) ? 0 : (nodes.length - 1));
        for (i = startIndex; i <= endIndex; i++) {
            nodes[i].viewIndex = i;
            nodes[i].viewRecordId = records[i].internalId;
            if (!nodes[i].boundView) {
                nodes[i].boundView = this.id;
            }
        }
    },

    
    getStore : function() {
        return this.store;
    },

    
    bindStore : function(store, initial, propName) {
        var me = this;
        me.mixins.bindable.bindStore.apply(me, arguments);

        
        
        if (!initial) {
            me.getSelectionModel().bindStore(store);
        }

        
        
        
        if (me.componentLayoutCounter) {
            me.doFirstRefresh(store);
        }
    },

    
    doFirstRefresh: function(store) {
        var me = this;

        
        me.firstRefreshDone = true;

        
        
        
        
        
        if (store && !store.loading) {
            if (me.deferInitialRefresh) {
                me.applyFirstRefresh();
            } else {
                me.refresh();
            }
        }
    },
    
    applyFirstRefresh: function(){
        var me = this;
        if (me.isDestroyed) {
            return;
        }
        
        
        
        
        
        
        
        
        
        if (me.up('[isCollapsingOrExpanding]')) {
            Ext.Function.defer(me.applyFirstRefresh, 100, me);
        } else {
            Ext.Function.defer(function () {
                if (!me.isDestroyed) {
                    me.refresh();
                }
            }, 1);
        }
    },

    onUnbindStore: function(store) {
        this.setMaskBind(null);
    },

    onBindStore: function(store, initial, propName) {
        this.setMaskBind(store);

        
        
        if (!initial && propName === 'store') {
            this.bindStore(store, false, 'dataSource');
        }
    },

    setMaskBind: function(store) {
        var mask = this.loadMask;
        if (mask && mask.bindStore) {
            mask.bindStore(store);
        }
    },

    getStoreListeners: function() {
        var me = this;
        return {
            idchanged: me.onIdChanged,
            refresh: me.onDataRefresh,
            add: me.onAdd,
            bulkremove: me.onRemove,
            update: me.onUpdate,
            clear: me.refresh
        };
    },

    
    onDataRefresh: function() {
        this.refreshView();
    },
    
    refreshView: function() {
        var me = this,
            
            
            blockedByAncestor = !me.firstRefreshDone && (!me.rendered || me.up('[collapsed],[isCollapsingOrExpanding],[hidden]'));

        
        
        
        
        if (blockedByAncestor) {
            me.deferInitialRefresh = false;
        } else if (me.blockRefresh !== true) {
            me.firstRefreshDone = true;
            me.refresh();
        }
    },

    
    findItemByChild: function(node){
        return Ext.fly(node).findParent(this.getItemSelector(), this.getTargetEl());
    },

    
    findTargetByEvent: function(e) {
        return e.getTarget(this.getItemSelector(), this.getTargetEl());
    },


    
    getSelectedNodes: function(){
        var nodes   = [],
            records = this.selModel.getSelection(),
            ln = records.length,
            i  = 0;

        for (; i < ln; i++) {
            nodes.push(this.getNode(records[i]));
        }

        return nodes;
    },

    
    getRecords: function(nodes) {
        var records = [],
            i = 0,
            len = nodes.length,
            data = this.dataSource.data;

        for (; i < len; i++) {
            records[records.length] = data.getByKey(nodes[i].viewRecordId);
        }

        return records;
    },

    
    getRecord: function(node){
        return this.dataSource.data.getByKey(Ext.getDom(node).viewRecordId);
    },


    
    isSelected : function(node) {
        
        var r = this.getRecord(node);
        return this.selModel.isSelected(r);
    },

    
    select: function(records, keepExisting, suppressEvent) {
        this.selModel.select(records, keepExisting, suppressEvent);
    },

    
    deselect: function(records, suppressEvent) {
        this.selModel.deselect(records, suppressEvent);
    },

    
    getNode : function(nodeInfo) {
        if ((!nodeInfo && nodeInfo !== 0) || !this.rendered) {
            return null;
        }

        if (Ext.isString(nodeInfo)) {
            return document.getElementById(nodeInfo);
        }
        if (Ext.isNumber(nodeInfo)) {
            return this.all.elements[nodeInfo];
        }
        if (nodeInfo.isModel) {
            return this.getNodeByRecord(nodeInfo);
        }
        return nodeInfo; 
    },

    
    getNodeByRecord: function(record) {
        var ns = this.all.elements,
            ln = ns.length,
            i = 0;

        for (; i < ln; i++) {
            if (ns[i].viewRecordId === record.internalId) {
                return ns[i];
            }
        }

        return null;
    },

    
    getNodes: function(start, end) {
        var all = this.all;

        if (end === undefined) {
            end = all.getCount();
        } else {
            end++;
        }
        return all.slice(start||0, end);
    },

    
    indexOf: function(node) {
        node = this.getNode(node);
        if (!node && node !== 0) {
            return -1;
        }
        if (Ext.isNumber(node.viewIndex)) {
            return node.viewIndex;
        }
        return this.all.indexOf(node);
    },

    onDestroy : function() {
        var me = this;

        me.all.clear();
        me.callParent();
        me.bindStore(null);
        me.selModel.destroy();
    },

    
    onItemSelect: function(record) {
        var node = this.getNode(record);

        if (node) {
            Ext.fly(node).addCls(this.selectedItemCls);
        }
    },

    
    onItemDeselect: function(record) {
        var node = this.getNode(record);

        if (node) {
            Ext.fly(node).removeCls(this.selectedItemCls);
        }
    },

    getItemSelector: function() {
        return this.itemSelector;
    }
}, function() {
    
    
    
    
    Ext.deprecate('extjs', '4.0', function() {
        Ext.view.AbstractView.override({
            
            
            

            
            getSelectionCount : function(){
                if (Ext.global.console) {
                    Ext.global.console.warn("DataView: getSelectionCount will be removed, please interact with the Ext.selection.DataViewModel");
                }
                return this.selModel.getSelection().length;
            },

            
            getSelectedRecords : function(){
                if (Ext.global.console) {
                    Ext.global.console.warn("DataView: getSelectedRecords will be removed, please interact with the Ext.selection.DataViewModel");
                }
                return this.selModel.getSelection();
            },

            
            
            select: function(records, keepExisting, supressEvents) {
                if (Ext.global.console) {
                    Ext.global.console.warn("DataView: select will be removed, please access select through a DataView's SelectionModel, ie: view.getSelectionModel().select()");
                }
                var sm = this.getSelectionModel();
                return sm.select.apply(sm, arguments);
            },

            
            clearSelections: function() {
                if (Ext.global.console) {
                    Ext.global.console.warn("DataView: clearSelections will be removed, please access deselectAll through DataView's SelectionModel, ie: view.getSelectionModel().deselectAll()");
                }
                var sm = this.getSelectionModel();
                return sm.deselectAll();
            }
        });
    });
});


Ext.define('Ext.view.View', {
    extend:  Ext.view.AbstractView ,
    alternateClassName: 'Ext.DataView',
    alias: 'widget.dataview',

    
    
    deferHighlight: Ext.isIE7m ? 100 : 0,

    
    mouseOverOutBuffer: 20,

    inputTagRe: /^textarea$|^input$/i,

    inheritableStatics: {
        EventMap: {
            mousedown: 'MouseDown',
            mouseup: 'MouseUp',
            click: 'Click',
            dblclick: 'DblClick',
            contextmenu: 'ContextMenu',
            mouseover: 'MouseOver',
            mouseout: 'MouseOut',
            mouseenter: 'MouseEnter',
            mouseleave: 'MouseLeave',
            keydown: 'KeyDown',
            focus: 'Focus'
        }
    },

    initComponent: function() {
        var me = this;
        me.callParent();

        
        if (me.mouseOverOutBuffer) {
            me.handleMouseOver =
                Ext.Function.createBuffered(me.handleMouseOver, me.mouseOverOutBuffer, me);
            me.handleMouseOut =
                Ext.Function.createBuffered(me.handleMouseOut, me.mouseOverOutBuffer, me);
            me.lastMouseOverEvent = new Ext.EventObjectImpl();
            me.lastMouseOutEvent = new Ext.EventObjectImpl();
        }

        
        else if (me.deferHighlight){
            me.setHighlightedItem =
                Ext.Function.createBuffered(me.setHighlightedItem, me.deferHighlight, me);
        }
    },

    addCmpEvents: function() {
        this.addEvents(
            
            'beforeitemmousedown',
            
            'beforeitemmouseup',
            
            'beforeitemmouseenter',
            
            'beforeitemmouseleave',
            
            'beforeitemclick',
            
            'beforeitemdblclick',
            
            'beforeitemcontextmenu',
            
            'beforeitemkeydown',
            
            'itemmousedown',
            
            'itemmouseup',
            
            'itemmouseenter',
            
            'itemmouseleave',
            
            'itemclick',
            
            'itemdblclick',
            
            'itemcontextmenu',
            
            'itemkeydown',
            
            'beforecontainermousedown',
            
            'beforecontainermouseup',
            
            'beforecontainermouseover',
            
            'beforecontainermouseout',
            
            'beforecontainerclick',
            
            'beforecontainerdblclick',
            
            'beforecontainercontextmenu',
            
            'beforecontainerkeydown',
            
            'containermouseup',
            
            'containermouseover',
            
            'containermouseout',
            
            'containerclick',
            
            'containerdblclick',
            
            'containercontextmenu',
            
            'containerkeydown',

            
            'selectionchange',
            
            'beforeselect',
            
            'beforedeselect',
            
            'select',
            
            'deselect',
            
            'focuschange',

            
            'highlightitem',

            
            'unhighlightitem'
        );
    },

    getFocusEl: function() {
        return this.getTargetEl();
    },

    
    afterRender: function() {
        var me = this,
            buffer = me.mouseOverOutBuffer;

        me.callParent();
        me.mon(me.getTargetEl(), {
            scope: me,
            
            freezeEvent: true,
            click: me.handleEvent,
            mousedown: me.handleEvent,
            mouseup: me.handleEvent,
            dblclick: me.handleEvent,
            contextmenu: me.handleEvent,
            keydown: me.handleEvent,
            mouseover: buffer ? me.onMouseOver : me.handleMouseOver,
            mouseout:  buffer ? me.onMouseOut : me.handleMouseOut
        });
    },

    onMouseOver: function(e) {
        var me = this;

        
        
        me.lastMouseOverEvent.setEvent(e.browserEvent, true);
        me.handleMouseOver(me.lastMouseOverEvent);
    },

    onMouseOut: function(e) {
        var me = this;

        
        
        if (!me._mouseOutPending) {
            me._mouseOutPending = true;
            me.lastMouseOutEvent.setEvent(e.browserEvent, true);
            me.handleMouseOut(me.lastMouseOutEvent);
        }
    },

    handleMouseOver: function(e) {
        var me = this,
            itemSelector = me.dataRowSelector || me.itemSelector,
            item = e.getTarget(itemSelector);

        
        if (!me.isDestroyed) {
            if (item) {
                if (me.mouseOverItem !== item && me.el.contains(item)) {
                    me.mouseOverItem = e.item = item;
                    e.newType = 'mouseenter';
                    me.handleEvent(e);
                }
            } else {
                
                e.item = e.newType = null;
                me.handleEvent(e);
            }
        }
    },

    handleMouseOut: function(e) {
        var me = this,
            itemSelector = me.dataRowSelector || me.itemSelector,
            item = e.getTarget(itemSelector),
            sourceView;

        
        if (!me.isDestroyed) {
            me._mouseOutPending = false;

            if (item) {
                if (e.getRelatedTarget(itemSelector) !== item && me.mouseOverItem === item) {
                    sourceView = me.self.getBoundView(item);
                    e.item = item;
                    e.newType = 'mouseleave';
                    sourceView.handleEvent(e);
                    sourceView.mouseOverItem = null;
                }
            } else {
                
                e.item = e.newType = null;
                me.handleEvent(e);
            }
        }

        
        e.newType = e.item = null;
    },

    handleEvent: function(e) {
        var me = this,
            key = e.type == 'keydown' && e.getKey();

        if (me.processUIEvent(e) !== false) {
            me.processSpecialEvent(e);
        }

        
        
        
        if (key === e.SPACE) {
            if (!me.inputTagRe.test(e.getTarget().tagName)) {
                e.stopEvent();
            }
        }
    },

    
    processItemEvent: Ext.emptyFn,
    processContainerEvent: Ext.emptyFn,
    processSpecialEvent: Ext.emptyFn,

    processUIEvent: function(e) {

        
        
        if (!Ext.getBody().isAncestor(e.target)) {
            return;
        }

        var me = this,
            item = e.getTarget(me.dataRowSelector || me.itemSelector, me.getTargetEl()),
            map = me.statics().EventMap,
            index, record,
            type = e.type,
            newType = e.type,
            sm;

        
        
        if (e.newType) {
            newType = e.newType;
            item = e.item;
        }

        
        
        if (!item && type == 'keydown') {
            sm = me.getSelectionModel();
            record = sm.lastFocused || sm.getLastSelected();
            if (record) {
                item = me.getNode(record, true);
            }
        }

        if (item) {
            if (!record) {
                record = me.getRecord(item);
            }
            index = me.indexInStore ? me.indexInStore(record) : me.indexOf(item);

            
            
            
            
            if (!record || me.processItemEvent(record, item, index, e) === false) {
                return false;
            }

            if (
                (me['onBeforeItem' + map[newType]](record, item, index, e) === false) ||
                (me.fireEvent('beforeitem' + newType, me, record, item, index, e) === false) ||
                (me['onItem' + map[newType]](record, item, index, e) === false)
            ) {
                return false;
            }

            me.fireEvent('item' + newType, me, record, item, index, e);
        }
        else {
            if (
                (me.processContainerEvent(e) === false) ||
                (me['onBeforeContainer' + map[type]](e) === false) ||
                (me.fireEvent('beforecontainer' + type, me, e) === false) ||
                (me['onContainer' + map[type]](e) === false)
            ) {
                return false;
            }

            me.fireEvent('container' + type, me, e);
        }

        return true;
    },

    
    onItemMouseEnter: function(record, item, index, e) {
        if (this.trackOver) {
            this.highlightItem(item);
        }
    },

    
    onItemMouseLeave : function(record, item, index, e) {
        if (this.trackOver) {
            this.clearHighlight();
        }
    },

    
    onItemMouseDown: Ext.emptyFn,
    onItemMouseUp: Ext.emptyFn,
    onItemFocus: Ext.emptyFn,
    onItemClick: Ext.emptyFn,
    onItemDblClick: Ext.emptyFn,
    onItemContextMenu: Ext.emptyFn,
    onItemKeyDown: Ext.emptyFn,
    onBeforeItemMouseDown: Ext.emptyFn,
    onBeforeItemMouseUp: Ext.emptyFn,
    onBeforeItemFocus: Ext.emptyFn,
    onBeforeItemMouseEnter: Ext.emptyFn,
    onBeforeItemMouseLeave: Ext.emptyFn,
    onBeforeItemClick: Ext.emptyFn,
    onBeforeItemDblClick: Ext.emptyFn,
    onBeforeItemContextMenu: Ext.emptyFn,
    onBeforeItemKeyDown: Ext.emptyFn,

    
    onContainerMouseDown: Ext.emptyFn,
    onContainerMouseUp: Ext.emptyFn,
    onContainerMouseOver: Ext.emptyFn,
    onContainerMouseOut: Ext.emptyFn,
    onContainerClick: Ext.emptyFn,
    onContainerDblClick: Ext.emptyFn,
    onContainerContextMenu: Ext.emptyFn,
    onContainerKeyDown: Ext.emptyFn,
    onBeforeContainerMouseDown: Ext.emptyFn,
    onBeforeContainerMouseUp: Ext.emptyFn,
    onBeforeContainerMouseOver: Ext.emptyFn,
    onBeforeContainerMouseOut: Ext.emptyFn,
    onBeforeContainerClick: Ext.emptyFn,
    onBeforeContainerDblClick: Ext.emptyFn,
    onBeforeContainerContextMenu: Ext.emptyFn,
    onBeforeContainerKeyDown: Ext.emptyFn,

    
    setHighlightedItem: function(item){
        var me = this,
            highlighted = me.highlightedItem,
            overItemCls = me.overItemCls,
            beforeOverItemCls = me.beforeOverItemCls,
            previous;

        if (highlighted != item){
            if (highlighted) {
                Ext.fly(highlighted).removeCls(overItemCls);
                previous = highlighted.previousSibling;
                if (beforeOverItemCls && previous) {
                    Ext.fly(previous).removeCls(beforeOverItemCls);
                }
                me.fireEvent('unhighlightitem', me, highlighted);
            }

            me.highlightedItem = item;

            if (item) {
                Ext.fly(item).addCls(me.overItemCls);
                previous = item.previousSibling;
                if (beforeOverItemCls && previous) {
                    Ext.fly(previous).addCls(beforeOverItemCls);
                }
                me.fireEvent('highlightitem', me, item);
            }
        }
    },

    
    highlightItem: function(item) {
        this.setHighlightedItem(item);
    },

    
    clearHighlight: function() {
        this.setHighlightedItem(undefined);
    },

    onUpdate: function(store, record){
        var me = this,
            node,
            newNode,
            highlighted;

        if (me.viewReady) {
            node = me.getNode(record);
            newNode = me.callParent(arguments);
            highlighted = me.highlightedItem;

            if (highlighted && highlighted === node) {
                delete me.highlightedItem;
                if (newNode) {
                    me.highlightItem(newNode);
                }
            }
        }
    },

    refresh: function() {
        this.clearHighlight();
        this.callParent(arguments);
    },

    
    focusNode: function(rec){
        var me          = this,
            node        = me.getNode(rec, true),
            el          = me.el,
            adjustmentY = 0,
            adjustmentX = 0,
            elRegion    = el.getRegion(),
            nodeRegion;

        
        
        elRegion.bottom = elRegion.top + el.dom.clientHeight;
        elRegion.right = elRegion.left + el.dom.clientWidth;
        if (node) {
            nodeRegion = Ext.fly(node).getRegion();
            
            if (nodeRegion.top < elRegion.top) {
                adjustmentY = nodeRegion.top - elRegion.top;
            
            } else if (nodeRegion.bottom > elRegion.bottom) {
                adjustmentY = nodeRegion.bottom - elRegion.bottom;
            }

            
            if (nodeRegion.left < elRegion.left) {
                adjustmentX = nodeRegion.left - elRegion.left;
            
            } else if (nodeRegion.right > elRegion.right) {
                adjustmentX = nodeRegion.right - elRegion.right;
            }

            if (adjustmentX || adjustmentY) {
                me.scrollBy(adjustmentX, adjustmentY, false);
            }
            el.focus();
        }
    },

    bindStore: function (store, initial, propertyName) {
        
        var dataSource = this[propertyName];

        if (dataSource && dataSource.isFeatureStore) {
            
            
            
            
            
            if (store.isFeatureStore) {
                
                this.bindStoreListeners(store);
                
                dataSource.bindStore(dataSource.store);
            } else {
                dataSource.bindStore(store);
            }
        } else {
            this.callParent(arguments);
        }
    }
});


Ext.define('Ext.layout.component.BoundList', {
    extend:  Ext.layout.component.Auto ,
    alias: 'layout.boundlist',

    type: 'component',
    
    beginLayout: function(ownerContext) {
        var me = this,
            owner = me.owner,
            toolbar = owner.pagingToolbar;

        me.callParent(arguments);
        
        if (owner.floating) {
            ownerContext.savedXY = owner.getXY();
            
            
            owner.setXY([0, -9999]);
        }
        
        if (toolbar) {
            ownerContext.toolbarContext = ownerContext.context.getCmp(toolbar);
        }
        ownerContext.listContext = ownerContext.getEl('listEl');
    },
    
    beginLayoutCycle: function(ownerContext){
        var owner = this.owner;
        
        this.callParent(arguments);
        if (ownerContext.heightModel.auto) {
            
            
            
            owner.el.setHeight('auto');
            owner.listEl.setHeight('auto');
        }
    },

    getLayoutItems: function() {
        var toolbar = this.owner.pagingToolbar;
        return toolbar ? [toolbar] : [];
    },
    
    isValidParent: function() {
        
        
        return true;
    },

    finishedLayout: function(ownerContext) {
        var xy = ownerContext.savedXY;
        
        this.callParent(arguments);
        if (xy) {
            this.owner.setXY(xy);
        }
    },
    
    measureContentWidth: function(ownerContext) {
        return this.owner.listEl.getWidth();
    },
    
    measureContentHeight: function(ownerContext) {
        return this.owner.listEl.getHeight();
    },
    
    publishInnerHeight: function(ownerContext, height) {
        var toolbar = ownerContext.toolbarContext,
            toolbarHeight = 0;
            
        if (toolbar) {
            toolbarHeight = toolbar.getProp('height');
        }
        
        if (toolbarHeight === undefined) {
            this.done = false;
        } else {
            ownerContext.listContext.setHeight(height - ownerContext.getFrameInfo().height - toolbarHeight);
        }
    },
    
    calculateOwnerHeightFromContentHeight: function(ownerContext){
        var height = this.callParent(arguments),
            toolbar = ownerContext.toolbarContext;
            
        if (toolbar) {
            height += toolbar.getProp('height');
        }
        return height;
    }
});


Ext.define('Ext.toolbar.TextItem', {
    extend:  Ext.toolbar.Item ,
                                
    alias: 'widget.tbtext',
    alternateClassName: 'Ext.Toolbar.TextItem',

    
    text: '',

    renderTpl: '{text}',
    
    baseCls: Ext.baseCSSPrefix + 'toolbar-text',
    
    ariaRole: null,

    beforeRender : function() {
        var me = this;

        me.callParent();

        Ext.apply(me.renderData, {
            text: me.text
        });
    },

    
    setText : function(text) {
        var me = this;
        me.text = text;
        if (me.rendered) {
            me.el.update(text);
            me.updateLayout();
        }
    }
});


Ext.define('Ext.form.field.Spinner', {
    extend:  Ext.form.field.Trigger ,
    alias: 'widget.spinnerfield',
    alternateClassName: 'Ext.form.Spinner',
                                  

    trigger1Cls: Ext.baseCSSPrefix + 'form-spinner-up',
    trigger2Cls: Ext.baseCSSPrefix + 'form-spinner-down',

    
    spinUpEnabled: true,

    
    spinDownEnabled: true,

    
    keyNavEnabled: true,

    
    mouseWheelEnabled: true,

    
    repeatTriggerClick: true,

    
    onSpinUp: Ext.emptyFn,

    
    onSpinDown: Ext.emptyFn,
    
    ariaRole: 'spinbutton',

    triggerTpl: '<td style="{triggerStyle}" class="{triggerCls}" role="presentation">' +
                    '<div class="' + Ext.baseCSSPrefix + 'trigger-index-0 ' + Ext.baseCSSPrefix + 'form-trigger ' + Ext.baseCSSPrefix + 'form-spinner-up {spinnerUpCls} {childElCls}" role="presentation"></div>' +
                    '<div class="' + Ext.baseCSSPrefix + 'trigger-index-1 ' + Ext.baseCSSPrefix + 'form-trigger ' + Ext.baseCSSPrefix + 'form-spinner-down {spinnerDownCls} {childElCls}" role="presentation"></div>' +
                '</td>' +
            '</tr>',

    initComponent: function() {
        this.callParent();

        this.addEvents(
            
            'spin',

            
            'spinup',

            
            'spindown'
        );
    },

    
    onRender: function() {
        var me = this,
            triggers;

        me.callParent(arguments);
        triggers = me.triggerEl;
        
        
        me.spinUpEl = triggers.item(0);
        
        me.spinDownEl = triggers.item(1);
        
        me.triggerCell = me.spinUpEl.parent(); 

        
        if (me.keyNavEnabled) {
            me.spinnerKeyNav = new Ext.util.KeyNav(me.inputEl, {
                scope: me,
                up: me.spinUp,
                down: me.spinDown
            });
        }

        
        if (me.mouseWheelEnabled) {
            me.mon(me.bodyEl, 'mousewheel', me.onMouseWheel, me);
        }
    },

    getSubTplMarkup: function(values) {
        var me = this,
            childElCls = values.childElCls, 
            field = Ext.form.field.Base.prototype.getSubTplMarkup.apply(me, arguments);

        return '<table id="' + me.id + '-triggerWrap" class="' + Ext.baseCSSPrefix + 'form-trigger-wrap' + childElCls + '" cellpadding="0" cellspacing="0" role="presentation">' +
            '<tbody role="presentation">' +
                '<tr role="presentation"><td id="' + me.id + '-inputCell" class="' + Ext.baseCSSPrefix + 'form-trigger-input-cell' + childElCls + '" role="presentation">' + field + '</td>' +
                me.getTriggerMarkup() +
            '</tbody></table>';
    },

    getTriggerMarkup: function() {
        return this.getTpl('triggerTpl').apply(this.getTriggerData());
    },
    
    getTriggerData: function(){
        var me = this,
            hideTrigger = (me.readOnly || me.hideTrigger);
            
        return {
            triggerCls: Ext.baseCSSPrefix + 'trigger-cell',
            triggerStyle: hideTrigger ? 'display:none' : '',
            spinnerUpCls: !me.spinUpEnabled ? me.trigger1Cls + '-disabled': '',
            spinnerDownCls: !me.spinDownEnabled ? me.trigger2Cls + '-disabled': ''
        };
    },

    
    getTriggerWidth: function() {
        var me = this,
            totalTriggerWidth = 0;

        if (me.triggerWrap && !me.hideTrigger && !me.readOnly) {
            totalTriggerWidth = me.triggerWidth;
        }
        return totalTriggerWidth;
    },

    
    onTrigger1Click: function() {
        this.spinUp();
    },

    
    onTrigger2Click: function() {
        this.spinDown();
    },

    
    
    onTriggerWrapMouseup: function() {
        this.inputEl.focus();
    },

    
    spinUp: function() {
        var me = this;
        if (me.spinUpEnabled && !me.disabled) {
            me.fireEvent('spin', me, 'up');
            me.fireEvent('spinup', me);
            me.onSpinUp();
        }
    },

    
    spinDown: function() {
        var me = this;
        if (me.spinDownEnabled && !me.disabled) {
            me.fireEvent('spin', me, 'down');
            me.fireEvent('spindown', me);
            me.onSpinDown();
        }
    },

    
    setSpinUpEnabled: function(enabled) {
        var me = this,
            wasEnabled = me.spinUpEnabled;
        me.spinUpEnabled = enabled;
        if (wasEnabled !== enabled && me.rendered) {
            me.spinUpEl[enabled ? 'removeCls' : 'addCls'](me.trigger1Cls + '-disabled');
        }
    },

    
    setSpinDownEnabled: function(enabled) {
        var me = this,
            wasEnabled = me.spinDownEnabled;
        me.spinDownEnabled = enabled;
        if (wasEnabled !== enabled && me.rendered) {
            me.spinDownEl[enabled ? 'removeCls' : 'addCls'](me.trigger2Cls + '-disabled');
        }
    },

    
    onMouseWheel: function(e) {
        var me = this,
            delta;
        if (me.hasFocus) {
            delta = e.getWheelDelta();
            if (delta > 0) {
                me.spinUp();
            } else if (delta < 0) {
                me.spinDown();
            }
            e.stopEvent();
        }
    },

    onDestroy: function() {
        Ext.destroyMembers(this, 'spinnerKeyNav', 'spinUpEl', 'spinDownEl');
        this.callParent();
    }

});


Ext.define('Ext.form.field.Number', {
    extend: Ext.form.field.Spinner ,
    alias: 'widget.numberfield',
    alternateClassName: ['Ext.form.NumberField', 'Ext.form.Number'],

    
    
     
    
    allowExponential: true,

    
    allowDecimals : true,

    
    
    decimalSeparator : null,
    
    
    
    
    submitLocaleSeparator: true,
    

    
    
    decimalPrecision : 2,
    

    
    minValue: Number.NEGATIVE_INFINITY,

    
    maxValue: Number.MAX_VALUE,

    
    step: 1,

    
    
    minText : 'The minimum value for this field is {0}',
    

    
    
    maxText : 'The maximum value for this field is {0}',
    

    
    
    nanText : '{0} is not a valid number',
    

    
    
    negativeText : 'The value cannot be negative',
    

    
    baseChars : '0123456789',

    
    autoStripChars: false,

    initComponent: function() {
        var me = this;
        if (me.decimalSeparator === null) {
            me.decimalSeparator = Ext.util.Format.decimalSeparator;
        }
        me.callParent();

        me.setMinValue(me.minValue);
        me.setMaxValue(me.maxValue);
    },

    
    getErrors: function(value) {
        var me = this,
            errors = me.callParent(arguments),
            format = Ext.String.format,
            num;

        value = Ext.isDefined(value) ? value : this.processRawValue(this.getRawValue());

        if (value.length < 1) { 
             return errors;
        }

        value = String(value).replace(me.decimalSeparator, '.');

        if(isNaN(value)){
            errors.push(format(me.nanText, value));
        }

        num = me.parseValue(value);

        if (me.minValue === 0 && num < 0) {
            errors.push(this.negativeText);
        }
        else if (num < me.minValue) {
            errors.push(format(me.minText, me.minValue));
        }

        if (num > me.maxValue) {
            errors.push(format(me.maxText, me.maxValue));
        }


        return errors;
    },

    rawToValue: function(rawValue) {
        var value = this.fixPrecision(this.parseValue(rawValue));
        if (value === null) {
            value = rawValue || null;
        }
        return  value;
    },

    valueToRaw: function(value) {
        var me = this,
            decimalSeparator = me.decimalSeparator;
        value = me.parseValue(value);
        value = me.fixPrecision(value);
        value = Ext.isNumber(value) ? value : parseFloat(String(value).replace(decimalSeparator, '.'));
        value = isNaN(value) ? '' : String(value).replace('.', decimalSeparator);
        return value;
    },
    
    getSubmitValue: function() {
        var me = this,
            value = me.callParent();
            
        if (!me.submitLocaleSeparator) {
            value = value.replace(me.decimalSeparator, '.');
        }  
        return value;
    },

    onChange: function() {
        this.toggleSpinners();
        this.callParent(arguments);
    },
    
    toggleSpinners: function(){
        var me = this,
            value = me.getValue(),
            valueIsNull = value === null,
            enabled;
        
        
        
        if (me.spinUpEnabled || me.spinUpDisabledByToggle) {
            enabled = valueIsNull || value < me.maxValue;
            me.setSpinUpEnabled(enabled, true);
        }
        
        
        if (me.spinDownEnabled || me.spinDownDisabledByToggle) {
            enabled = valueIsNull || value > me.minValue;
            me.setSpinDownEnabled(enabled, true);
        }
    },

    
    setMinValue : function(value) {
        var me = this,
            allowed;
        
        me.minValue = Ext.Number.from(value, Number.NEGATIVE_INFINITY);
        me.toggleSpinners();
        
        
        if (me.disableKeyFilter !== true) {
            allowed = me.baseChars + '';
            
            if (me.allowExponential) {
                allowed += me.decimalSeparator + 'e+-';
            }
            else {
                if (me.allowDecimals) {
                    allowed += me.decimalSeparator;
                }
                if (me.minValue < 0) {
                    allowed += '-';
                }
            }
            
            allowed = Ext.String.escapeRegex(allowed);
            me.maskRe = new RegExp('[' + allowed + ']');
            if (me.autoStripChars) {
                me.stripCharsRe = new RegExp('[^' + allowed + ']', 'gi');
            }
        }
    },

    
    setMaxValue: function(value) {
        this.maxValue = Ext.Number.from(value, Number.MAX_VALUE);
        this.toggleSpinners();
    },

    
    parseValue : function(value) {
        value = parseFloat(String(value).replace(this.decimalSeparator, '.'));
        return isNaN(value) ? null : value;
    },

    
    fixPrecision : function(value) {
        var me = this,
            nan = isNaN(value),
            precision = me.decimalPrecision;

        if (nan || !value) {
            return nan ? '' : value;
        } else if (!me.allowDecimals || precision <= 0) {
            precision = 0;
        }

        return parseFloat(Ext.Number.toFixed(parseFloat(value), precision));
    },

    beforeBlur : function() {
        var me = this,
            v = me.parseValue(me.getRawValue());

        if (!Ext.isEmpty(v)) {
            me.setValue(v);
        }
    },
    
    setSpinUpEnabled: function(enabled,  internal){
        this.callParent(arguments);
        if (!internal) {
            delete this.spinUpDisabledByToggle;
        } else {
            this.spinUpDisabledByToggle = !enabled;
        }
    },

    onSpinUp: function() {
        var me = this;
            
        if (!me.readOnly) {
            me.setSpinValue(Ext.Number.constrain(me.getValue() + me.step, me.minValue, me.maxValue));
        }
    },
    
    setSpinDownEnabled: function(enabled,  internal){
        this.callParent(arguments);
        if (!internal) {
            delete this.spinDownDisabledByToggle;
        } else {
            this.spinDownDisabledByToggle = !enabled;
        }   
    },

    onSpinDown: function() {
        var me = this;
        
        if (!me.readOnly) {
            me.setSpinValue(Ext.Number.constrain(me.getValue() - me.step, me.minValue, me.maxValue));
        }
    },
    
    setSpinValue: function(value) {
        var me = this,
            len;
            
        if (me.enforceMaxLength) {
            
            
            if (me.fixPrecision(value).toString().length > me.maxLength) {
                return;
            }
        }
        me.setValue(value);
    }
});


Ext.define('Ext.toolbar.Paging', {
    extend:  Ext.toolbar.Toolbar ,
    alias: 'widget.pagingtoolbar',
    alternateClassName: 'Ext.PagingToolbar',
                                                                
    mixins: {
        bindable:  Ext.util.Bindable     
    },
    

    
    displayInfo: false,

    
    prependButtons: false,

    
    
    displayMsg : 'Displaying {0} - {1} of {2}',
    

    
    
    emptyMsg : 'No data to display',
    

    
    
    beforePageText : 'Page',
    

    
    
    afterPageText : 'of {0}',
    

    
    
    firstText : 'First Page',
    

    
    
    prevText : 'Previous Page',
    

    
    
    nextText : 'Next Page',
    

    
    
    lastText : 'Last Page',
    

    
    
    refreshText : 'Refresh',
    

    
    inputItemWidth : 30,

    
    getPagingItems: function() {
        var me = this;
        return [{
            itemId: 'first',
            tooltip: me.firstText,
            overflowText: me.firstText,
            iconCls: Ext.baseCSSPrefix + 'tbar-page-first',
            disabled: true,
            handler: me.moveFirst,
            scope: me
        },{
            itemId: 'prev',
            tooltip: me.prevText,
            overflowText: me.prevText,
            iconCls: Ext.baseCSSPrefix + 'tbar-page-prev',
            disabled: true,
            handler: me.movePrevious,
            scope: me
        },
        '-',
        me.beforePageText,
        {
            xtype: 'numberfield',
            itemId: 'inputItem',
            name: 'inputItem',
            cls: Ext.baseCSSPrefix + 'tbar-page-number',
            allowDecimals: false,
            minValue: 1,
            hideTrigger: true,
            enableKeyEvents: true,
            keyNavEnabled: false,
            selectOnFocus: true,
            submitValue: false,
            
            isFormField: false,
            width: me.inputItemWidth,
            margins: '-1 2 3 2',
            listeners: {
                scope: me,
                keydown: me.onPagingKeyDown,
                blur: me.onPagingBlur
            }
        },{
            xtype: 'tbtext',
            itemId: 'afterTextItem',
            text: Ext.String.format(me.afterPageText, 1)
        },
        '-',
        {
            itemId: 'next',
            tooltip: me.nextText,
            overflowText: me.nextText,
            iconCls: Ext.baseCSSPrefix + 'tbar-page-next',
            disabled: true,
            handler: me.moveNext,
            scope: me
        },{
            itemId: 'last',
            tooltip: me.lastText,
            overflowText: me.lastText,
            iconCls: Ext.baseCSSPrefix + 'tbar-page-last',
            disabled: true,
            handler: me.moveLast,
            scope: me
        },
        '-',
        {
            itemId: 'refresh',
            tooltip: me.refreshText,
            overflowText: me.refreshText,
            iconCls: Ext.baseCSSPrefix + 'tbar-loading',
            disabled: me.store.isLoading(),
            handler: me.doRefresh,
            scope: me
        }];
    },

    initComponent : function(){
        var me = this,
            userItems = me.items || me.buttons || [],
            pagingItems;

        me.bindStore(me.store || 'ext-empty-store', true);
        pagingItems = me.getPagingItems();
        if (me.prependButtons) {
            me.items = userItems.concat(pagingItems);
        } else {
            me.items = pagingItems.concat(userItems);
        }
        delete me.buttons;

        if (me.displayInfo) {
            me.items.push('->');
            me.items.push({xtype: 'tbtext', itemId: 'displayItem'});
        }

        me.callParent();

        me.addEvents(
            
            'change',

            
            'beforechange'
        );
    },
    
    beforeRender: function() {
        this.callParent(arguments);
        if (!this.store.isLoading()) {
            this.onLoad();    
        }    
    },
    
    
    updateInfo : function(){
        var me = this,
            displayItem = me.child('#displayItem'),
            store = me.store,
            pageData = me.getPageData(),
            count, msg;

        if (displayItem) {
            count = store.getCount();
            if (count === 0) {
                msg = me.emptyMsg;
            } else {
                msg = Ext.String.format(
                    me.displayMsg,
                    pageData.fromRecord,
                    pageData.toRecord,
                    pageData.total
                );
            }
            displayItem.setText(msg);
        }
    },

    
    onLoad : function(){
        var me = this,
            pageData,
            currPage,
            pageCount,
            afterText,
            count,
            isEmpty,
            item;

        count = me.store.getCount();
        isEmpty = count === 0;
        if (!isEmpty) {
            pageData = me.getPageData();
            currPage = pageData.currentPage;
            pageCount = pageData.pageCount;
            
             
            if (currPage > pageCount) {
                me.store.loadPage(pageCount);
                return;
            }
            
            afterText = Ext.String.format(me.afterPageText, isNaN(pageCount) ? 1 : pageCount);
        } else {
            currPage = 0;
            pageCount = 0;
            afterText = Ext.String.format(me.afterPageText, 0);
        }

        Ext.suspendLayouts();
        item = me.child('#afterTextItem');
        if (item) {    
            item.setText(afterText);
        }
        item = me.getInputItem();
        if (item) {
            item.setDisabled(isEmpty).setValue(currPage);
        }
        me.setChildDisabled('#first', currPage === 1 || isEmpty);
        me.setChildDisabled('#prev', currPage === 1 || isEmpty);
        me.setChildDisabled('#next', currPage === pageCount  || isEmpty);
        me.setChildDisabled('#last', currPage === pageCount  || isEmpty);
        me.setChildDisabled('#refresh', false);
        me.updateInfo();
        Ext.resumeLayouts(true);

        me.fireEvent('change', me, pageData);
    },
    
    setChildDisabled: function(selector, disabled){
        var item = this.child(selector);
        if (item) {
            item.setDisabled(disabled);
        }
    },

    
    getPageData : function(){
        var store = this.store,
            totalCount = store.getTotalCount();

        return {
            total : totalCount,
            currentPage : store.currentPage,
            pageCount: Math.ceil(totalCount / store.pageSize),
            fromRecord: ((store.currentPage - 1) * store.pageSize) + 1,
            toRecord: Math.min(store.currentPage * store.pageSize, totalCount)

        };
    },

    
    onLoadError : function(){
        this.setChildDisabled('#refresh', false);
    },
    
    getInputItem: function(){
        return this.child('#inputItem');
    },

    
    readPageFromInput : function(pageData){
        var inputItem = this.getInputItem(),
            pageNum = false,
            v;

        if (inputItem) {
            v = inputItem.getValue();
            pageNum = parseInt(v, 10);
            if (!v || isNaN(pageNum)) {
                inputItem.setValue(pageData.currentPage);
                return false;
            }
        }
        return pageNum;
    },

    
    onPagingBlur : function(e){
        var inputItem = this.getInputItem(),
            curPage;
            
        if (inputItem) {
            curPage = this.getPageData().currentPage;
            inputItem.setValue(curPage);
        }
    },

    
    onPagingKeyDown : function(field, e){
        this.processKeyEvent(field, e);
    },
    
    processKeyEvent: function(field, e) {
        var me = this,
            k = e.getKey(),
            pageData = me.getPageData(),
            increment = e.shiftKey ? 10 : 1,
            pageNum;

        if (k == e.RETURN) {
            e.stopEvent();
            pageNum = me.readPageFromInput(pageData);
            if (pageNum !== false) {
                pageNum = Math.min(Math.max(1, pageNum), pageData.pageCount);
                if (pageNum !== pageData.currentPage && me.fireEvent('beforechange', me, pageNum) !== false) {
                    me.store.loadPage(pageNum);
                }
            }
        } else if (k == e.HOME || k == e.END) {
            e.stopEvent();
            pageNum = k == e.HOME ? 1 : pageData.pageCount;
            field.setValue(pageNum);
        } else if (k == e.UP || k == e.PAGE_UP || k == e.DOWN || k == e.PAGE_DOWN) {
            e.stopEvent();
            pageNum = me.readPageFromInput(pageData);
            if (pageNum) {
                if (k == e.DOWN || k == e.PAGE_DOWN) {
                    increment *= -1;
                }
                pageNum += increment;
                if (pageNum >= 1 && pageNum <= pageData.pageCount) {
                    field.setValue(pageNum);
                }
            }
        }    
    },

    
    beforeLoad : function() {
        this.setChildDisabled('#refresh', true);
    },

    
    moveFirst : function(){
        if (this.fireEvent('beforechange', this, 1) !== false){
            this.store.loadPage(1);
            return true;
        }
        return false;
    },

    
    movePrevious : function(){
        var me = this,
            store = me.store,
            prev = store.currentPage - 1;

        if (prev > 0) {
            if (me.fireEvent('beforechange', me, prev) !== false) {
                store.previousPage();
                return true;
            }
        }
        return false;
    },

    
    moveNext : function(){
        var me = this,
            store = me.store,
            total = me.getPageData().pageCount,
            next = store.currentPage + 1;

        if (next <= total) {
            if (me.fireEvent('beforechange', me, next) !== false) {
                store.nextPage();
                return true;
            }
        }
        return false;
    },

    
    moveLast : function(){
        var me = this,
            last = me.getPageData().pageCount;

        if (me.fireEvent('beforechange', me, last) !== false) {
            me.store.loadPage(last);
            return true;
        }
        return false;
    },

    
    doRefresh : function(){
        var me = this,
            store = me.store,
            current = store.currentPage;

        if (me.fireEvent('beforechange', me, current) !== false) {
            store.loadPage(current);
            return true;
        }
        return false;
    },
    
    getStoreListeners: function() {
        return {
            beforeload: this.beforeLoad,
            load: this.onLoad,
            exception: this.onLoadError
        };
    },

    
    unbind : function(store){
        this.bindStore(null);
    },

    
    bind : function(store){
        this.bindStore(store);
    },

    
    onDestroy : function(){
        this.unbind();
        this.callParent();
    }
});


Ext.define('Ext.view.BoundList', {
    extend:  Ext.view.View ,
    alias: 'widget.boundlist',
    alternateClassName: 'Ext.BoundList',
                                                                       
    
    mixins: {
        queryable:  Ext.Queryable 
    },

    
    pageSize: 0,

    

    

    
    baseCls: Ext.baseCSSPrefix + 'boundlist',
    itemCls: Ext.baseCSSPrefix + 'boundlist-item',
    listItemCls: '',
    shadow: false,
    trackOver: true,
    refreshed: 0,

    
    deferInitialRefresh: false,

    componentLayout: 'boundlist',

    childEls: [
        'listEl'
    ],

    renderTpl: [
        '<div id="{id}-listEl" role="presentation" class="{baseCls}-list-ct ', Ext.dom.Element.unselectableCls, '" style="overflow:auto"></div>',
        '{%',
            'var me=values.$comp, pagingToolbar=me.pagingToolbar;',
            'if (pagingToolbar) {',
                'pagingToolbar.ownerLayout = me.componentLayout;',
                'Ext.DomHelper.generateMarkup(pagingToolbar.getRenderTree(), out);',
            '}',
        '%}',
        {
            disableFormats: true
        }
    ],

    

    initComponent: function() {
        var me = this,
            baseCls = me.baseCls,
            itemCls = me.itemCls;

        me.selectedItemCls = baseCls + '-selected';
        if (me.trackOver) {
            me.overItemCls = baseCls + '-item-over';
        }
        me.itemSelector = "." + itemCls;

        if (me.floating) {
            me.addCls(baseCls + '-floating');
        }

        if (!me.tpl) {
            
            
            me.tpl = new Ext.XTemplate(
                '<ul class="' + Ext.plainListCls + '"><tpl for=".">',
                    '<li role="option" unselectable="on" class="' + itemCls + '">' + me.getInnerTpl(me.displayField) + '</li>',
                '</tpl></ul>'
            );
        } else if (!me.tpl.isTemplate) {
            me.tpl = new Ext.XTemplate(me.tpl);
        }

        if (me.pageSize) {
            me.pagingToolbar = me.createPagingToolbar();
        }

        me.callParent();
    },

    beforeRender: function() {
        var me = this;

        me.callParent(arguments);

        
        
        if (me.up('menu')) {
            me.addCls(Ext.baseCSSPrefix + 'menu');
        }
    },

    getRefOwner: function() {
        return this.pickerField || this.callParent();
    },

    getRefItems: function() {
        return this.pagingToolbar ? [ this.pagingToolbar ] : [];
    },

    createPagingToolbar: function() {
        return Ext.widget('pagingtoolbar', {
            id: this.id + '-paging-toolbar',
            pageSize: this.pageSize,
            store: this.dataSource,
            border: false,
            ownerCt: this,
            ownerLayout: this.getComponentLayout()
        });
    },

    
    
    finishRenderChildren: function () {
        var toolbar = this.pagingToolbar;

        this.callParent(arguments);

        if (toolbar) {
            toolbar.finishRender();
        }
    },

    refresh: function(){
        var me = this,
            tpl = me.tpl,
            toolbar = me.pagingToolbar,
            rendered = me.rendered;

        
        tpl.field = me.pickerField;
        tpl.store = me.store;
        me.callParent();
        tpl.field =  tpl.store = null;

        
        
        if (rendered && toolbar && toolbar.rendered && !me.preserveScrollOnRefresh) {
            me.el.appendChild(toolbar.el);
        }

        
        
        if (rendered && Ext.isIE6 && Ext.isStrict) {
            me.listEl.repaint();
        }
    },

    bindStore : function(store, initial) {
        var toolbar = this.pagingToolbar;

        this.callParent(arguments);
        if (toolbar) {
            toolbar.bindStore(store, initial);
        }
    },

    getTargetEl: function() {
        return this.listEl || this.el;
    },

    
    getInnerTpl: function(displayField) {
        return '{' + displayField + '}';
    },

    onDestroy: function() {
        Ext.destroyMembers(this, 'pagingToolbar', 'listEl');
        this.callParent();
    }
});


Ext.define('Ext.view.BoundListKeyNav', {
    extend:  Ext.util.KeyNav ,
                                   

    

    constructor: function(el, config) {
        var me = this;
        me.boundList = config.boundList;
        me.callParent([el, Ext.apply({}, config, me.defaultHandlers)]);
    },

    defaultHandlers: {
        up: function() {
            var me = this,
                boundList = me.boundList,
                allItems = boundList.all,
                oldItem = boundList.highlightedItem,
                oldItemIdx = oldItem ? boundList.indexOf(oldItem) : -1,
                newItemIdx = oldItemIdx > 0 ? oldItemIdx - 1 : allItems.getCount() - 1; 
            me.highlightAt(newItemIdx);
        },

        down: function() {
            var me = this,
                boundList = me.boundList,
                allItems = boundList.all,
                oldItem = boundList.highlightedItem,
                oldItemIdx = oldItem ? boundList.indexOf(oldItem) : -1,
                newItemIdx = oldItemIdx < allItems.getCount() - 1 ? oldItemIdx + 1 : 0; 
            me.highlightAt(newItemIdx);
        },

        pageup: function() {
            
        },

        pagedown: function() {
            
        },

        home: function() {
            this.highlightAt(0);
        },

        end: function() {
            var me = this;
            me.highlightAt(me.boundList.all.getCount() - 1);
        },

        enter: function(e) {
            this.selectHighlighted(e);
        }
    },

    
    highlightAt: function(index) {
        var boundList = this.boundList,
            item = boundList.all.item(index);
        if (item) {
            item = item.dom;
            boundList.highlightItem(item);
            boundList.getTargetEl().scrollChildIntoView(item, false);
        }
    },

    
    selectHighlighted: function(e) {
        var boundList = this.boundList,
            selModel = boundList.getSelectionModel(),
            highlighted, highlightedRec;

        highlighted = boundList.highlightedItem;
        if (highlighted) {
            highlightedRec = boundList.getRecord(highlighted);    

            
            
            if (e.getKey() === e.ENTER || !selModel.isSelected(highlightedRec)) {
                selModel.selectWithEvent(highlightedRec, e);
            }
        }
    }

});


Ext.define('Ext.layout.component.field.ComboBox', {
    extend:  Ext.layout.component.field.Trigger ,
    alias: 'layout.combobox',
                                       

    type: 'combobox',

    startingWidth: null,

    getTextWidth: function () {
        var me = this,
            owner = me.owner,
            store = owner.store,
            field = owner.displayField,
            storeLn = store.data.length,
            value = '',
            i = 0, n = 0, ln, item, width;

        for (; i < storeLn; i++) {
            item = store.getAt(i).data[field];
            ln = item.length;
            
            if (ln > n) {
                n = ln;
                value = item;
            }
        }

        width = Math.max(me.callParent(arguments), owner.inputEl.getTextWidth(value + owner.growAppend));

        
        
        if (!me.startingWidth || owner.removingRecords) {
            me.startingWidth = width;

            
            
            if (width < owner.growMin) {
                owner.defaultListConfig.minWidth = owner.growMin;
            }

            owner.removingRecords = false;
        }
 
        
        return (width < me.startingWidth) ? me.startingWidth : width;
    }
});


Ext.define('Ext.form.field.ComboBox', {
    extend: Ext.form.field.Picker ,
                                                                                                                                                                            
    alternateClassName: 'Ext.form.ComboBox',
    alias: ['widget.combobox', 'widget.combo'],
    mixins: {
        bindable:  Ext.util.Bindable     
    },

    componentLayout: 'combobox',

    

    
    triggerCls: Ext.baseCSSPrefix + 'form-arrow-trigger',
    
    
    hiddenName: '',
    
    

    
    hiddenDataCls: Ext.baseCSSPrefix + 'hide-display ' + Ext.baseCSSPrefix + 'form-data-hidden',
    
    ariaRole: 'combobox',

    
    fieldSubTpl: [
        '<div class="{hiddenDataCls}" role="presentation"></div>',
        '<input id="{id}" type="{type}" role="{role}" {inputAttrTpl} class="{fieldCls} {typeCls} {editableCls}" autocomplete="off"',
            '<tpl if="value"> value="{[Ext.util.Format.htmlEncode(values.value)]}"</tpl>',
            '<tpl if="name"> name="{name}"</tpl>',
            '<tpl if="placeholder"> placeholder="{placeholder}"</tpl>',
            '<tpl if="size"> size="{size}"</tpl>',
            '<tpl if="maxLength !== undefined"> maxlength="{maxLength}"</tpl>',
            '<tpl if="readOnly"> readonly="readonly"</tpl>',
            '<tpl if="disabled"> disabled="disabled"</tpl>',
            '<tpl if="tabIdx"> tabIndex="{tabIdx}"</tpl>',
            '<tpl if="fieldStyle"> style="{fieldStyle}"</tpl>',
            '/>',
        {
            compiled: true,
            disableFormats: true
        }
    ],

    getSubTplData: function(){
        var me = this;
        Ext.applyIf(me.subTplData, {
            hiddenDataCls: me.hiddenDataCls
        });
        return me.callParent(arguments);
    },

    afterRender: function(){
        var me = this;
        me.callParent(arguments);
        me.setHiddenValue(me.value);
    },

    

    
    multiSelect: false,

    
    
    delimiter: ', ',
    

    
    displayField: 'text',

    

    
    triggerAction: 'all',

    
    allQuery: '',

    
    queryParam: 'query',

    
    queryMode: 'remote',

    
    queryCaching: true,

    
    pageSize: 0,

    

    

    
    anyMatch: false,

    
    caseSensitive: false,

    
    autoSelect: true,

    
    typeAhead: false,

    
    typeAheadDelay: 250,

    
    selectOnTab: true,

    
    forceSelection: false,

    
    growToLongestValue: true,
    
    
    clearFilterOnBlur: true,
    
    

    

    

    
    defaultListConfig: {
        loadingHeight: 70,
        minWidth: 70,
        maxHeight: 300,
        shadow: 'sides'
    },

    
    
    
    transformInPlace: true,

    

    
    ignoreSelection: 0,

    
    removingRecords: null,

    
    resizeComboToGrow: function () {
        var me = this;
        return me.grow && me.growToLongestValue;
    },

    initComponent: function() {
        var me = this,
            isDefined = Ext.isDefined,
            store = me.store,
            transform = me.transform,
            displayTpl = me.displayTpl,
            transformSelect, isLocalMode;

        Ext.applyIf(me.renderSelectors, {
            hiddenDataEl: '.' + me.hiddenDataCls.split(' ').join('.')
        });
        

        me.addEvents(
            
            'beforequery',

            
            'select',

            
            'beforeselect',

            
            'beforedeselect'
        );

        
        if (transform) {
            transformSelect = Ext.getDom(transform);
            if (transformSelect) {
                if (!me.store) {
                    store = Ext.Array.map(Ext.Array.from(transformSelect.options), function(option){
                        return [option.value, option.text];
                    });
                }
                if (!me.name) {
                    me.name = transformSelect.name;
                }
                if (!('value' in me)) {
                    me.value = transformSelect.value;
                }
            }
        }

        me.bindStore(store || 'ext-empty-store', true);
        store = me.store;
        if (store.autoCreated) {
            me.queryMode = 'local';
            me.valueField = me.displayField = 'field1';
            if (!store.expanded) {
                me.displayField = 'field2';
            }
        }

        if (!isDefined(me.valueField)) {
            me.valueField = me.displayField;
        }

        isLocalMode = me.queryMode === 'local';
        if (!isDefined(me.queryDelay)) {
            me.queryDelay = isLocalMode ? 10 : 500;
        }
        if (!isDefined(me.minChars)) {
            me.minChars = isLocalMode ? 0 : 4;
        }

        if (!displayTpl) {
            me.displayTpl = new Ext.XTemplate(
                '<tpl for=".">' +
                    '{[typeof values === "string" ? values : values["' + me.displayField + '"]]}' +
                    '<tpl if="xindex < xcount">' + me.delimiter + '</tpl>' +
                '</tpl>'
            );
        } else if (!displayTpl.isTemplate) {
            me.displayTpl = new Ext.XTemplate(displayTpl);
        }

        me.callParent();

        me.doQueryTask = new Ext.util.DelayedTask(me.doRawQuery, me);

        
        if (me.store.getCount() > 0) {
            me.setValue(me.value);
        }

        
        if (transformSelect) {
            if (me.transformInPlace) {
                me.render(transformSelect.parentNode, transformSelect);
                delete me.renderTo;
            }
            Ext.removeNode(transformSelect);
        }
    },

    
    getStore : function(){
        return this.store;
    },

    beforeBlur: function() {
        var me = this,
            filter = me.queryFilter;
            
        me.doQueryTask.cancel();
        me.assertValue();
        
        if (filter && !filter.disabled && me.queryMode === 'local' && me.clearFilterOnBlur) {
            filter.disabled = true;
            me.store.filter();
        }
    },
    
    onFocus: function() {
        var me = this,
            filter = me.queryFilter;
        
        me.callParent(arguments);    
        if (!me.duringTriggerClick && me.triggerAction !== 'all' && filter && filter.disabled && me.queryMode === 'local' && me.clearFilterOnBlur) {
            delete me.lastQuery;
            me.doRawQuery();
        }
    },

    
    assertValue: function() {
        var me = this,
            value = me.getRawValue(),
            rec, currentValue;

        if (me.forceSelection) {
            if (me.multiSelect) {
                
                
                if (value !== me.getDisplayValue()) {
                    me.setValue(me.lastSelection);
                }
            } else {
                
                
                rec = me.findRecordByDisplay(value);
                if (rec) {
                    currentValue = me.value;
                    
                    
                    if (!me.findRecordByValue(currentValue)) {
                        me.select(rec, true);
                    }
                } else {
                    me.setValue(me.lastSelection);
                }
            }
        }
        me.collapse();
    },

    onTypeAhead: function() {
        var me = this,
            displayField = me.displayField,
            record = me.store.findRecord(displayField, me.getRawValue()),
            boundList = me.getPicker(),
            newValue, len, selStart;

        if (record) {
            newValue = record.get(displayField);
            len = newValue.length;
            selStart = me.getRawValue().length;

            boundList.highlightItem(boundList.getNode(record));

            if (selStart !== 0 && selStart !== len) {
                me.setRawValue(newValue);
                me.selectText(selStart, newValue.length);
            }
        }
    },

    
    
    resetToDefault: Ext.emptyFn,

    beforeReset: function() {
        var filter = this.queryFilter;
        
        this.callParent();

        
        if (filter && !filter.disabled) {
            filter.disabled = true;
            this.store.filter();
        }
    },

    onUnbindStore: function(store) {
        var me = this,
            picker = me.picker,
            filter = me.queryFilter;

        
        if (filter) {
            me.store.removeFilter(filter);
        }
        if (!store && picker) {
            picker.bindStore(null);
        }
    },

    onBindStore: function(store, initial) {
        var picker = this.picker;
        if (!initial) {
            this.resetToDefault();
        }

        if (picker) {
            picker.bindStore(store);
        }
    },

    getStoreListeners: function() {
        var me = this;

        return {
            beforeload: me.onBeforeLoad,
            clear: me.onClear,
            datachanged: me.onDataChanged,
            load: me.onLoad,
            exception: me.onException,
            remove: me.onRemove
        }; 
    },

    onBeforeLoad: function(){
        
        
        
        ++this.ignoreSelection;    
    },

    onDataChanged: function() {
        var me = this;

        if (me.resizeComboToGrow()) {
            me.updateLayout();
        }
    },

    onClear: function() {
        var me = this;

        if (me.resizeComboToGrow()) {
            me.removingRecords = true;
            me.onDataChanged();
        }
    },

    onRemove: function() {
        var me = this;

        if (me.resizeComboToGrow()) {
            me.removingRecords = true;
        }
    },

    onException: function(){
        if (this.ignoreSelection > 0) {
            --this.ignoreSelection;
        }
        this.collapse();    
    },

    onLoad: function(store, records, success) {
        var me = this;

        if (me.ignoreSelection > 0) {
            --me.ignoreSelection;
        }

        
        if (success && !store.lastOptions.rawQuery) {
            

            
            if (me.value == null) {
                
                if (me.store.getCount()) {
                    me.doAutoSelect();
                } else {
                    
                    me.setValue(me.value);
                }
            } else {
                me.setValue(me.value);
            }
        }
    },

    
    doRawQuery: function() {
        this.doQuery(this.getRawValue(), false, true);
    },

    
    doQuery: function(queryString, forceAll, rawQuery) {
        var me = this,

            
            queryPlan = me.beforeQuery({
                query: queryString || '',
                rawQuery: rawQuery,
                forceAll: forceAll,
                combo: me,
                cancel: false
            });

        
        if (queryPlan === false || queryPlan.cancel) {
            return false;
        }

        
        if (me.queryCaching && queryPlan.query === me.lastQuery) {
            me.expand();
            if (me.queryMode === 'local') {
                me.doAutoSelect();
            }
        }
        
        
        else {
            me.lastQuery = queryPlan.query;

            if (me.queryMode === 'local') {
                me.doLocalQuery(queryPlan);

            } else {
                me.doRemoteQuery(queryPlan);
            }
        }

        return true;
    },

    
    beforeQuery: function(queryPlan) {
        var me = this;

        
        if (me.fireEvent('beforequery', queryPlan) === false) {
            queryPlan.cancel = true;
        }

        
        else if (!queryPlan.cancel) {

            
            if (queryPlan.query.length < me.minChars && !queryPlan.forceAll) {
                queryPlan.cancel = true;
            }
        }
        return queryPlan;
    },

    doLocalQuery: function(queryPlan) {
        var me = this,
            queryString = queryPlan.query;

        
        if (!me.queryFilter) {
            
            me.queryFilter = new Ext.util.Filter({
                id: me.id + '-query-filter',
                anyMatch: me.anyMatch,
                caseSensitive: me.caseSensitive,
                root: 'data',
                property: me.displayField
            });
            me.store.addFilter(me.queryFilter, false);
        }

        
        if (queryString || !queryPlan.forceAll) {
            me.queryFilter.disabled = false;
            me.queryFilter.setValue(me.enableRegEx ? new RegExp(queryString) : queryString);
        }

        
        else {
            me.queryFilter.disabled = true;
        }

        
        me.store.filter();

        
        if (me.store.getCount()) {
            me.expand();
        } else {
            me.collapse();
        }

        me.afterQuery(queryPlan);
    },

    doRemoteQuery: function(queryPlan) {
        var me = this,
            loadCallback = function() {
                me.afterQuery(queryPlan);
            };

        
        me.expand();

        
        
        if (me.pageSize) {
            
            me.loadPage(1, {
                rawQuery: queryPlan.rawQuery,
                callback: loadCallback
            });
        } else {
            me.store.load({
                params: me.getParams(queryPlan.query),
                rawQuery: queryPlan.rawQuery,
                callback: loadCallback
            });
        }
    },

    
    afterQuery: function(queryPlan) {
        var me = this;

        if (me.store.getCount()) {
            if (me.typeAhead) {
                me.doTypeAhead();
            }

            
            if (me.getRawValue() !== me.getDisplayValue()) {
                me.ignoreSelection++;
                me.picker.getSelectionModel().deselectAll();
                me.ignoreSelection--;
            }

            if (queryPlan.rawQuery) {
                me.syncSelection();
                if (me.picker && !me.picker.getSelectionModel().hasSelection()) {
                    me.doAutoSelect();
                }
            } else {
                me.doAutoSelect();
            }
        }
    },

    loadPage: function(pageNum, options) {
        this.store.loadPage(pageNum, Ext.apply({
            params: this.getParams(this.lastQuery)
        }, options));
    },

    onPageChange: function(toolbar, newPage){
        
        this.loadPage(newPage);
        return false;
    },

    
    getParams: function(queryString) {
        var params = {},
            param = this.queryParam;

        if (param) {
            params[param] = queryString;
        }
        return params;
    },

    
    doAutoSelect: function() {
        var me = this,
            picker = me.picker,
            lastSelected, itemNode;
        if (picker && me.autoSelect && me.store.getCount() > 0) {
            
            lastSelected = picker.getSelectionModel().lastSelected;
            itemNode = picker.getNode(lastSelected || 0);
            if (itemNode) {
                picker.highlightItem(itemNode);
                picker.listEl.scrollChildIntoView(itemNode, false);
            }
        }
    },

    doTypeAhead: function() {
        var me = this;
        if (!me.typeAheadTask) {
            me.typeAheadTask = new Ext.util.DelayedTask(me.onTypeAhead, me);
        }
        if (me.lastKey != Ext.EventObject.BACKSPACE && me.lastKey != Ext.EventObject.DELETE) {
            me.typeAheadTask.delay(me.typeAheadDelay);
        }
    },

    onTriggerClick: function() {
        var me = this;
        
        me.duringTriggerClick = true;
        if (!me.readOnly && !me.disabled) {
            if (me.isExpanded) {
                me.collapse();
            } else {
                me.onFocus({});
                if (me.triggerAction === 'all') {
                    me.doQuery(me.allQuery, true);
                } else if (me.triggerAction === 'last') {
                    me.doQuery(me.lastQuery, true);
                } else {
                    me.doQuery(me.getRawValue(), false, true);
                }
            }
            me.inputEl.focus();
        }
        delete me.duringTriggerClick;
    },

    onPaste: function(){
        var me = this;
        
        if (!me.readOnly && !me.disabled && me.editable) {
            me.doQueryTask.delay(me.queryDelay);
        }
    },

    
    onKeyUp: function(e, t) {
        var me = this,
            key = e.getKey();

        if (!me.readOnly && !me.disabled && me.editable) {
            me.lastKey = key;
            
            

            
            if (!e.isSpecialKey() || key == e.BACKSPACE || key == e.DELETE) {
                me.doQueryTask.delay(me.queryDelay);
            }
        }

        if (me.enableKeyEvents) {
            me.callParent(arguments);
        }
    },

    initEvents: function() {
        var me = this;
        me.callParent();

        
        if (!me.enableKeyEvents) {
            me.mon(me.inputEl, 'keyup', me.onKeyUp, me);
        }
        me.mon(me.inputEl, 'paste', me.onPaste, me);
    },

    onDestroy: function() {
        var me = this
        
        if (me.typeAheadTask) {
            me.typeAheadTask.cancel();
            me.typeAheadTask = null;
        }
        
        Ext.destroy(me.listKeyNav);
        me.bindStore(null);
        me.callParent();
    },

    
    
    onAdded: function() {
        var me = this;
        me.callParent(arguments);
        if (me.picker) {
            me.picker.ownerCt = me.up('[floating]');
            me.picker.registerWithOwnerCt();
        }
    },

    createPicker: function() {
        var me = this,
            picker,
            pickerCfg = Ext.apply({
                xtype: 'boundlist',
                pickerField: me,
                selModel: {
                    mode: me.multiSelect ? 'SIMPLE' : 'SINGLE'
                },
                floating: true,
                hidden: true,
                store: me.store,
                displayField: me.displayField,
                focusOnToFront: false,
                pageSize: me.pageSize,
                tpl: me.tpl
            }, me.listConfig, me.defaultListConfig);

        picker = me.picker = Ext.widget(pickerCfg);
        if (me.pageSize) {
            picker.pagingToolbar.on('beforechange', me.onPageChange, me);
        }

        me.mon(picker, {
            itemclick: me.onItemClick,
            refresh: me.onListRefresh,
            scope: me
        });

        me.mon(picker.getSelectionModel(), {
            beforeselect: me.onBeforeSelect,
            beforedeselect: me.onBeforeDeselect,
            selectionchange: me.onListSelectionChange,
            scope: me
        });

        return picker;
    },

    alignPicker: function(){
        var me = this,
            picker = me.getPicker(),
            heightAbove = me.getPosition()[1] - Ext.getBody().getScroll().top,
            heightBelow = Ext.Element.getViewHeight() - heightAbove - me.getHeight(),
            space = Math.max(heightAbove, heightBelow);

        
        if (picker.height) {
            delete picker.height;
            picker.updateLayout();
        }
        
        if (picker.getHeight() > space - 5) {
            picker.setHeight(space - 5); 
        }
        me.callParent();
    },

    onListRefresh: function() {
        
        if (!this.expanding) {
            this.alignPicker();
        }
        this.syncSelection();
    },

    onItemClick: function(picker, record){
        
        var me = this,
            selection = me.picker.getSelectionModel().getSelection(),
            valueField = me.valueField;

        if (!me.multiSelect && selection.length) {
            if (record.get(valueField) === selection[0].get(valueField)) {
                
                me.displayTplData = [record.data];
                me.setRawValue(me.getDisplayValue());
                me.collapse();
            }
        }
    },

    onBeforeSelect: function(list, record) {
        return this.fireEvent('beforeselect', this, record, record.index);
    },

    onBeforeDeselect: function(list, record) {
        return this.fireEvent('beforedeselect', this, record, record.index);
    },

    onListSelectionChange: function(list, selectedRecords) {
        var me = this,
            isMulti = me.multiSelect,
            hasRecords = selectedRecords.length > 0;
        
        
        if (!me.ignoreSelection && me.isExpanded) {
            if (!isMulti) {
                Ext.defer(me.collapse, 1, me);
            }
            
            if (isMulti || hasRecords) {
                me.setValue(selectedRecords, false);
            }
            if (hasRecords) {
                me.fireEvent('select', me, selectedRecords);
            }
            me.inputEl.focus();
        }
    },

    
    onExpand: function() {
        var me = this,
            keyNav = me.listKeyNav,
            selectOnTab = me.selectOnTab,
            picker = me.getPicker();

        
        if (keyNav) {
            keyNav.enable();
        } else {
            keyNav = me.listKeyNav = new Ext.view.BoundListKeyNav(me.inputEl, {
                boundList: picker,
                forceKeyDown: true,
                tab: function(e) {
                    if (selectOnTab) {
                        this.selectHighlighted(e);
                        me.triggerBlur();
                    }
                    
                    return true;
                },
                enter: function(e){
                    var selModel = picker.getSelectionModel(),
                        count = selModel.getCount();
                        
                    this.selectHighlighted(e);
                    
                    
                    
                    if (!me.multiSelect && count === selModel.getCount()) {
                        me.collapse();
                    }
                }
            });
        }

        
        if (selectOnTab) {
            me.ignoreMonitorTab = true;
        }

        Ext.defer(keyNav.enable, 1, keyNav); 
        me.inputEl.focus();
    },

    
    onCollapse: function() {
        var me = this,
            keyNav = me.listKeyNav;
        if (keyNav) {
            keyNav.disable();
            me.ignoreMonitorTab = false;
        }
    },

    
    select: function(r,  assert) {
        var me = this,
            picker = me.picker,
            fireSelect;
        
        if (r && r.isModel && assert === true && picker) {
            fireSelect = !picker.getSelectionModel().isSelected(r);
        }
        
        me.setValue(r, true);
        
        
        if (fireSelect) {
            me.fireEvent('select', me, r);
        }
    },

    
    findRecord: function(field, value) {
        var ds = this.store,
            idx = ds.findExact(field, value);
        return idx !== -1 ? ds.getAt(idx) : false;
    },

    
    findRecordByValue: function(value) {
        return this.findRecord(this.valueField, value);
    },

    
    findRecordByDisplay: function(value) {
        return this.findRecord(this.displayField, value);
    },

    
    setValue: function(value, doSelect) {
        var me = this,
            valueNotFoundText = me.valueNotFoundText,
            inputEl = me.inputEl,
            i, len, record,
            dataObj,
            matchedRecords = [],
            displayTplData = [],
            processedValue = [];

        if (me.store.loading) {
            
            me.value = value;
            me.setHiddenValue(me.value);
            return me;
        }

        
        value = Ext.Array.from(value);

        
        for (i = 0, len = value.length; i < len; i++) {
            record = value[i];
            if (!record || !record.isModel) {
                record = me.findRecordByValue(record);
            }
            
            if (record) {
                matchedRecords.push(record);
                displayTplData.push(record.data);
                processedValue.push(record.get(me.valueField));
            }
            
            
            else {
                
                
                if (!me.forceSelection) {
                    processedValue.push(value[i]);
                    dataObj = {};
                    dataObj[me.displayField] = value[i];
                    displayTplData.push(dataObj);
                    
                }
                
                else if (Ext.isDefined(valueNotFoundText)) {
                    displayTplData.push(valueNotFoundText);
                }
            }
        }

        
        me.setHiddenValue(processedValue);
        me.value = me.multiSelect ? processedValue : processedValue[0];
        if (!Ext.isDefined(me.value)) {
            me.value = null;
        }
        me.displayTplData = displayTplData; 
        me.lastSelection = me.valueModels = matchedRecords;

        if (inputEl && me.emptyText && !Ext.isEmpty(value)) {
            inputEl.removeCls(me.emptyCls);
        }

        
        me.setRawValue(me.getDisplayValue());
        me.checkChange();

        if (doSelect !== false) {
            me.syncSelection();
        }
        me.applyEmptyText();

        return me;
    },

    
    setHiddenValue: function(values){
        var me = this,
            name = me.hiddenName, 
            i,
            dom, childNodes, input, valueCount, childrenCount;
            
        if (!me.hiddenDataEl || !name) {
            return;
        }
        values = Ext.Array.from(values);
        dom = me.hiddenDataEl.dom;
        childNodes = dom.childNodes;
        input = childNodes[0];
        valueCount = values.length;
        childrenCount = childNodes.length;
        
        if (!input && valueCount > 0) {
            me.hiddenDataEl.update(Ext.DomHelper.markup({
                tag: 'input', 
                type: 'hidden', 
                name: name
            }));
            childrenCount = 1;
            input = dom.firstChild;
        }
        while (childrenCount > valueCount) {
            dom.removeChild(childNodes[0]);
            -- childrenCount;
        }
        while (childrenCount < valueCount) {
            dom.appendChild(input.cloneNode(true));
            ++ childrenCount;
        }
        for (i = 0; i < valueCount; i++) {
            childNodes[i].value = values[i];
        }
    },

    
    getDisplayValue: function() {
        return this.displayTpl.apply(this.displayTplData);
    },

    getValue: function() {
        
        
        
        var me = this,
            picker = me.picker,
            rawValue = me.getRawValue(), 
            value = me.value; 

        if (me.getDisplayValue() !== rawValue) {
            value = rawValue;
            me.value = me.displayTplData = me.valueModels = null;
            if (picker) {
                me.ignoreSelection++;
                picker.getSelectionModel().deselectAll();
                me.ignoreSelection--;
            }
        }

        return value;
    },

    getSubmitValue: function() {
        var value = this.getValue();
        
        
        if (Ext.isEmpty(value)) {
            value = '';
        }
        return value;
    },

    isEqual: function(v1, v2) {
        var fromArray = Ext.Array.from,
            i, len;

        v1 = fromArray(v1);
        v2 = fromArray(v2);
        len = v1.length;

        if (len !== v2.length) {
            return false;
        }

        for(i = 0; i < len; i++) {
            if (v2[i] !== v1[i]) {
                return false;
            }
        }

        return true;
    },

    
    clearValue: function() {
        this.setValue([]);
    },

    
    syncSelection: function() {
        var me = this,
            picker = me.picker,
            selection, selModel,
            values = me.valueModels || [],
            vLen  = values.length, v, value;

        if (picker) {
            
            selection = [];
            for (v = 0; v < vLen; v++) {
                value = values[v];

                if (value && value.isModel && me.store.indexOf(value) >= 0) {
                    selection.push(value);
                }
            }

            
            me.ignoreSelection++;
            selModel = picker.getSelectionModel();
            selModel.deselectAll();
            if (selection.length) {
                selModel.select(selection, undefined, true);
            }
            me.ignoreSelection--;
        }
    },
    
    onEditorTab: function(e){
        var keyNav = this.listKeyNav;
        
        if (this.selectOnTab && keyNav) {
            keyNav.selectHighlighted(e);
        }
    }
});


Ext.define('Ext.picker.Month', {
    extend:  Ext.Component ,
               
                         
                                  
                   
                           
      
    alias: 'widget.monthpicker',
    alternateClassName: 'Ext.MonthPicker',

    childEls: [
        'bodyEl', 'prevEl', 'nextEl', 'buttonsEl', 'monthEl', 'yearEl'
    ],

    renderTpl: [
        '<div id="{id}-bodyEl" class="{baseCls}-body">',
          '<div id="{id}-monthEl" class="{baseCls}-months">',
              '<tpl for="months">',
                  '<div class="{parent.baseCls}-item {parent.baseCls}-month">',
                      
                      '<a style="{parent.monthStyle}" role="button" hidefocus="on" class="{parent.baseCls}-item-inner" href="#">{.}</a>',
                  '</div>',
              '</tpl>',
          '</div>',
          '<div id="{id}-yearEl" class="{baseCls}-years">',
              '<div class="{baseCls}-yearnav">',
                  '<div class="{baseCls}-yearnav-button-ct">',
                      
                      '<a id="{id}-prevEl" class="{baseCls}-yearnav-button {baseCls}-yearnav-prev" href="#" hidefocus="on" role="button"></a>',
                  '</div>',
                  '<div class="{baseCls}-yearnav-button-ct">',
                      
                      '<a id="{id}-nextEl" class="{baseCls}-yearnav-button {baseCls}-yearnav-next" href="#" hidefocus="on" role="button"></a>',
                  '</div>',
              '</div>',
              '<tpl for="years">',
                  '<div class="{parent.baseCls}-item {parent.baseCls}-year">',
                      
                      '<a hidefocus="on" class="{parent.baseCls}-item-inner" role="button" href="#">{.}</a>',
                  '</div>',
              '</tpl>',
          '</div>',
          '<div class="' + Ext.baseCSSPrefix + 'clear"></div>',
          '<tpl if="showButtons">',
              '<div id="{id}-buttonsEl" class="{baseCls}-buttons">{%',
                  'var me=values.$comp, okBtn=me.okBtn, cancelBtn=me.cancelBtn;',
                  'okBtn.ownerLayout = cancelBtn.ownerLayout = me.componentLayout;',
                  'okBtn.ownerCt = cancelBtn.ownerCt = me;',
                  'Ext.DomHelper.generateMarkup(okBtn.getRenderTree(), out);',
                  'Ext.DomHelper.generateMarkup(cancelBtn.getRenderTree(), out);',
              '%}</div>',
          '</tpl>',
        '</div>'
    ],

    
    
    okText: 'OK',
    

    
    
    cancelText: 'Cancel',
    

    
    baseCls: Ext.baseCSSPrefix + 'monthpicker',

    
    showButtons: true,

    

    
    
    measureWidth: 35,
    measureMaxHeight: 20,

    
    smallCls: Ext.baseCSSPrefix + 'monthpicker-small',

    
    totalYears: 10,
    yearOffset: 5, 
    monthOffset: 6, 

    
    
    initComponent: function(){
        var me = this;

        me.selectedCls = me.baseCls + '-selected';
        me.addEvents(
            
            'cancelclick',

            
            'monthclick',

            
            'monthdblclick',

            
            'okclick',

            
            'select',

            
            'yearclick',

            
            'yeardblclick'
        );
        if (me.small) {
            me.addCls(me.smallCls);
        }
        me.setValue(me.value);
        me.activeYear = me.getYear(new Date().getFullYear() - 4, -4);

        if (me.showButtons) {
            me.okBtn = new Ext.button.Button({
                text: me.okText,
                handler: me.onOkClick,
                scope: me
            });
            me.cancelBtn = new Ext.button.Button({
                text: me.cancelText,
                handler: me.onCancelClick,
                scope: me
            });
        }

        this.callParent();
    },

    
    
    beforeRender: function(){
        var me = this,
            i = 0,
            months = [],
            shortName = Ext.Date.getShortMonthName,
            monthLen = me.monthOffset,
            margin = me.monthMargin,
            style = '';

        if (me.padding && !me.width) {
            me.cacheWidth();
        }

        me.callParent();

        for (; i < monthLen; ++i) {
            months.push(shortName(i), shortName(i + monthLen));
        }
        
        if (Ext.isDefined(margin)) {
            style = 'margin: 0 ' + margin + 'px;';
        }

        Ext.apply(me.renderData, {
            months: months,
            years: me.getYears(),
            showButtons: me.showButtons,
            monthStyle: style
        });
    },

    cacheWidth: function() {
        var me = this,
            padding = me.parseBox(me.padding),
            widthEl = Ext.getBody().createChild({
                cls: me.baseCls + ' ' + me.borderBoxCls,
                style: 'position:absolute;top:-1000px;left:-1000px;',
                html: '&nbsp;' 
            });

        me.self.prototype.width = widthEl.getWidth() + padding.left + padding.right;
        widthEl.remove();
    },

    
    
    afterRender: function(){
        var me = this,
            body = me.bodyEl,
            buttonsEl = me.buttonsEl;

        me.callParent();

        me.mon(body, 'click', me.onBodyClick, me);
        me.mon(body, 'dblclick', me.onBodyClick, me);

        
        me.years = body.select('.' + me.baseCls + '-year a');
        me.months = body.select('.' + me.baseCls + '-month a');

        me.backRepeater = new Ext.util.ClickRepeater(me.prevEl, {
            handler: Ext.Function.bind(me.adjustYear, me, [-me.totalYears])
        });

        me.prevEl.addClsOnOver(me.baseCls + '-yearnav-prev-over');
        me.nextRepeater = new Ext.util.ClickRepeater(me.nextEl, {
            handler: Ext.Function.bind(me.adjustYear, me, [me.totalYears])
        });
        me.nextEl.addClsOnOver(me.baseCls + '-yearnav-next-over');
        me.updateBody();
        
        if (!Ext.isDefined(me.monthMargin)) {
            Ext.picker.Month.prototype.monthMargin = me.calculateMonthMargin();
        }
    },
    
    calculateMonthMargin: function(){
        
        
        
        
        var me = this,
            monthEl = me.monthEl,
            months = me.months,
            first = months.first(),
            itemMargin = first.getMargin('l');
            
        while (itemMargin && me.getLargest() > me.measureMaxHeight) {
            --itemMargin;
            months.setStyle('margin', '0 ' + itemMargin + 'px');
        }
        return itemMargin;
    },
    
    getLargest: function(months){
        var largest = 0;
        this.months.each(function(item){
            var h = item.getHeight();
            if (h > largest) {
                largest = h;
            }
        });
        return largest;
        
    },

    
    setValue: function(value){
        var me = this,
            active = me.activeYear,
            offset = me.monthOffset,
            year,
            index;

        if (!value) {
            me.value = [null, null];
        } else if (Ext.isDate(value)) {
            me.value = [value.getMonth(), value.getFullYear()];
        } else {
            me.value = [value[0], value[1]];
        }

        if (me.rendered) {
            year = me.value[1];
            if (year !== null) {
                if ((year < active || year > active + me.yearOffset)) {
                    me.activeYear = year - me.yearOffset + 1;
                }
            }
            me.updateBody();
        }

        return me;
    },

    
    getValue: function(){
        return this.value;
    },

    
    hasSelection: function(){
        var value = this.value;
        return value[0] !== null && value[1] !== null;
    },

    
    getYears: function(){
        var me = this,
            offset = me.yearOffset,
            start = me.activeYear, 
            end = start + offset,
            i = start,
            years = [];

        for (; i < end; ++i) {
            years.push(i, i + offset);
        }

        return years;
    },

    
    updateBody: function(){
        var me = this,
            years = me.years,
            months = me.months,
            yearNumbers = me.getYears(),
            cls = me.selectedCls,
            value = me.getYear(null),
            month = me.value[0],
            monthOffset = me.monthOffset,
            year,
            yearItems, y, yLen, el;

        if (me.rendered) {
            years.removeCls(cls);
            months.removeCls(cls);

            yearItems = years.elements;
            yLen      = yearItems.length;

            for (y = 0; y < yLen; y++) {
                el = Ext.fly(yearItems[y]);

                year = yearNumbers[y];
                el.dom.innerHTML = year;
                if (year == value) {
                    el.addCls(cls);
                }
            }
            if (month !== null) {
                if (month < monthOffset) {
                    month = month * 2;
                } else {
                    month = (month - monthOffset) * 2 + 1;
                }
                months.item(month).addCls(cls);
            }
        }
    },

    
    getYear: function(defaultValue, offset) {
        var year = this.value[1];
        offset = offset || 0;
        return year === null ? defaultValue : year + offset;
    },

    
    onBodyClick: function(e, t) {
        var me = this,
            isDouble = e.type == 'dblclick';

        if (e.getTarget('.' + me.baseCls + '-month')) {
            e.stopEvent();
            me.onMonthClick(t, isDouble);
        } else if (e.getTarget('.' + me.baseCls + '-year')) {
            e.stopEvent();
            me.onYearClick(t, isDouble);
        }
    },

    
    adjustYear: function(offset){
        if (typeof offset != 'number') {
            offset = this.totalYears;
        }
        this.activeYear += offset;
        this.updateBody();
    },

    
    onOkClick: function(){
        this.fireEvent('okclick', this, this.value);
    },

    
    onCancelClick: function(){
        this.fireEvent('cancelclick', this);
    },

    
    onMonthClick: function(target, isDouble){
        var me = this;
        me.value[0] = me.resolveOffset(me.months.indexOf(target), me.monthOffset);
        me.updateBody();
        me.fireEvent('month' + (isDouble ? 'dbl' : '') + 'click', me, me.value);
        me.fireEvent('select', me, me.value);
    },

    
    onYearClick: function(target, isDouble){
        var me = this;
        me.value[1] = me.activeYear + me.resolveOffset(me.years.indexOf(target), me.yearOffset);
        me.updateBody();
        me.fireEvent('year' + (isDouble ? 'dbl' : '') + 'click', me, me.value);
        me.fireEvent('select', me, me.value);

    },

    
    resolveOffset: function(index, offset){
        if (index % 2 === 0) {
            return (index / 2);
        } else {
            return offset + Math.floor(index / 2);
        }
    },

    
    
    beforeDestroy: function(){
        var me = this;
        me.years = me.months = null;
        Ext.destroyMembers(me, 'backRepeater', 'nextRepeater', 'okBtn', 'cancelBtn');
        me.callParent();
    },

    
    
    finishRenderChildren: function () {
        var me = this;

        this.callParent(arguments);

        if (this.showButtons) {
            me.okBtn.finishRender();
            me.cancelBtn.finishRender();
        }
    },

    onDestroy: function() {
        Ext.destroyMembers(this, 'okBtn', 'cancelBtn');
        this.callParent();
    }
    
});


Ext.define('Ext.picker.Date', {
    extend:  Ext.Component ,
               
                        
                            
                           
                                 
                          
                          
                         
                          
      
    alias: 'widget.datepicker',
    alternateClassName: 'Ext.DatePicker',

    childEls: [
        'innerEl', 'eventEl', 'prevEl', 'nextEl', 'middleBtnEl', 'footerEl'
    ],
    
    border: true,

    renderTpl: [
        '<div id="{id}-innerEl" role="grid">',
            '<div role="presentation" class="{baseCls}-header">',
                 
                '<a id="{id}-prevEl" class="{baseCls}-prev {baseCls}-arrow" href="#" role="button" title="{prevText}" hidefocus="on" ></a>',
                '<div class="{baseCls}-month" id="{id}-middleBtnEl">{%this.renderMonthBtn(values, out)%}</div>',
                 
                '<a id="{id}-nextEl" class="{baseCls}-next {baseCls}-arrow" href="#" role="button" title="{nextText}" hidefocus="on" ></a>',
            '</div>',
            '<table id="{id}-eventEl" class="{baseCls}-inner" cellspacing="0" role="grid">',
                '<thead role="presentation"><tr role="row">',
                    '<tpl for="dayNames">',
                        '<th role="columnheader" class="{parent.baseCls}-column-header" title="{.}">',
                            '<div class="{parent.baseCls}-column-header-inner">{.:this.firstInitial}</div>',
                        '</th>',
                    '</tpl>',
                '</tr></thead>',
                '<tbody role="presentation"><tr role="row">',
                    '<tpl for="days">',
                        '{#:this.isEndOfWeek}',
                        '<td role="gridcell" id="{[Ext.id()]}">',
                            
                            '<a role="button" hidefocus="on" class="{parent.baseCls}-date" href="#"></a>',
                        '</td>',
                    '</tpl>',
                '</tr></tbody>',
            '</table>',
            '<tpl if="showToday">',
                '<div id="{id}-footerEl" role="presentation" class="{baseCls}-footer">{%this.renderTodayBtn(values, out)%}</div>',
            '</tpl>',
        '</div>',
        {
            firstInitial: function(value) {
                return Ext.picker.Date.prototype.getDayInitial(value);
            },
            isEndOfWeek: function(value) {
                
                
                value--;
                var end = value % 7 === 0 && value !== 0;
                return end ? '</tr><tr role="row">' : '';
            },
            renderTodayBtn: function(values, out) {
                Ext.DomHelper.generateMarkup(values.$comp.todayBtn.getRenderTree(), out);
            },
            renderMonthBtn: function(values, out) {
                Ext.DomHelper.generateMarkup(values.$comp.monthBtn.getRenderTree(), out);
            }
        }
    ],

    
    
    todayText : 'Today',
    
    
    
    
    ariaTitle: 'Date Picker: {0}',
    
    
    
    
    ariaTitleDateFormat: 'F d, Y',
    

    

    

    
    
    todayTip : '{0} (Spacebar)',
    

    
    
    minText : 'This date is before the minimum date',
    

    
    
    maxText : 'This date is after the maximum date',
    

    

    
    
    disabledDaysText : 'Disabled',
    

    
    
    disabledDatesText : 'Disabled',
    

    

    

    
    
    nextText : 'Next Month (Control+Right)',
    

    
    
    prevText : 'Previous Month (Control+Left)',
    

    
    
    monthYearText : 'Choose a month (Control+Up/Down to move years)',
    
    
    
    
    monthYearFormat: 'F Y',
    

    
    
    startDay : 0,
    

    
    
    showToday : true,
    

    

    

    

    

    

    
    disableAnim: false,

    
    baseCls: Ext.baseCSSPrefix + 'datepicker',

    

    

    
    
    longDayFormat: 'F d, Y',
    

    

    
    focusOnShow: false,

    
    
    focusOnSelect: true,

    
    
    initHour: 12, 

    numDays: 42,

    
    initComponent : function() {
        var me = this,
            clearTime = Ext.Date.clearTime;

        me.selectedCls = me.baseCls + '-selected';
        me.disabledCellCls = me.baseCls + '-disabled';
        me.prevCls = me.baseCls + '-prevday';
        me.activeCls = me.baseCls + '-active';
        me.cellCls = me.baseCls + '-cell';
        me.nextCls = me.baseCls + '-prevday';
        me.todayCls = me.baseCls + '-today';
        
        
        if (!me.format) {
            me.format = Ext.Date.defaultFormat;
        }
        if (!me.dayNames) {
            me.dayNames = Ext.Date.dayNames;
        }
        me.dayNames = me.dayNames.slice(me.startDay).concat(me.dayNames.slice(0, me.startDay));

        me.callParent();

        me.value = me.value ?
                 clearTime(me.value, true) : clearTime(new Date());

        me.addEvents(
            
            'select'
        );

        me.initDisabledDays();
    },

    beforeRender: function () {
        
        var me = this,
            days = new Array(me.numDays),
            today = Ext.Date.format(new Date(), me.format);

        
        
        if (me.up('menu')) {
            me.addCls(Ext.baseCSSPrefix + 'menu');
        }

        if (me.padding && !me.width) {
            me.cacheWidth();
        }

        me.monthBtn = new Ext.button.Split({
            ownerCt: me,
            ownerLayout: me.getComponentLayout(),
            text: '',
            tooltip: me.monthYearText,
            listeners: {
                click: me.doShowMonthPicker,
                arrowclick: me.doShowMonthPicker,
                scope: me
            }
        });

        if (me.showToday) {
            me.todayBtn = new Ext.button.Button({
                ownerCt: me,
                ownerLayout: me.getComponentLayout(),
                text: Ext.String.format(me.todayText, today),
                tooltip: Ext.String.format(me.todayTip, today),
                tooltipType: 'title',
                handler: me.selectToday,
                scope: me
            });
        }

        me.callParent();

        Ext.applyIf(me, {
            renderData: {}
        });

        Ext.apply(me.renderData, {
            dayNames: me.dayNames,
            showToday: me.showToday,
            prevText: me.prevText,
            nextText: me.nextText,
            days: days
        });

        me.protoEl.unselectable();
    },

    cacheWidth: function() {
        var me = this,
            padding = me.parseBox(me.padding),
            widthEl = Ext.getBody().createChild({
                cls: me.baseCls + ' ' + me.borderBoxCls,
                style: 'position:absolute;top:-1000px;left:-1000px;'
            });

        me.self.prototype.width = widthEl.getWidth() + padding.left + padding.right;
        widthEl.remove();
    },

    
    
    finishRenderChildren: function () {
        var me = this;
        
        me.callParent();
        me.monthBtn.finishRender();
        if (me.showToday) {
            me.todayBtn.finishRender();
        }
    },

    
    
    onRender : function(container, position){
        var me = this;

        me.callParent(arguments);

        me.cells = me.eventEl.select('tbody td');
        me.textNodes = me.eventEl.query('tbody td a');
        
        me.mon(me.eventEl, {
            scope: me,
            mousewheel: me.handleMouseWheel,
            click: {
                fn: me.handleDateClick,
                delegate: 'a.' + me.baseCls + '-date'
            }
        });
        
    },

    
    
    initEvents: function(){
        var me = this,
            eDate = Ext.Date,
            day = eDate.DAY;

        me.callParent();

        me.prevRepeater = new Ext.util.ClickRepeater(me.prevEl, {
            handler: me.showPrevMonth,
            scope: me,
            preventDefault: true,
            stopDefault: true
        });

        me.nextRepeater = new Ext.util.ClickRepeater(me.nextEl, {
            handler: me.showNextMonth,
            scope: me,
            preventDefault:true,
            stopDefault:true
        });

        me.keyNav = new Ext.util.KeyNav(me.eventEl, Ext.apply({
            scope: me,
            left : function(e){
                if(e.ctrlKey){
                    me.showPrevMonth();
                }else{
                    me.update(eDate.add(me.activeDate, day, -1));
                }
            },

            right : function(e){
                if(e.ctrlKey){
                    me.showNextMonth();
                }else{
                    me.update(eDate.add(me.activeDate, day, 1));
                }
            },

            up : function(e){
                if(e.ctrlKey){
                    me.showNextYear();
                }else{
                    me.update(eDate.add(me.activeDate, day, -7));
                }
            },

            down : function(e){
                if(e.ctrlKey){
                    me.showPrevYear();
                }else{
                    me.update(eDate.add(me.activeDate, day, 7));
                }
            },

            pageUp:function (e) {
                if (e.altKey) {
                    me.showPrevYear();
                } else {
                    me.showPrevMonth();
                }
            },

            pageDown:function (e) {
                if (e.altKey) {
                    me.showNextYear();
                } else {
                    me.showNextMonth();
                }
            },

            tab:function (e) {
                me.doCancelFieldFocus = true;
                me.handleTabClick(e);
                delete me.doCancelFieldFocus;
                return true;
            },
            
            enter : function(e){
                e.stopPropagation();
                return true;
            },

            

            home:function (e) {
                me.update(eDate.getFirstDateOfMonth(me.activeDate));
            },

            end:function (e) {
                me.update(eDate.getLastDateOfMonth(me.activeDate));
            }
        }, me.keyNavConfig));

        if (me.showToday) {
            me.todayKeyListener = me.eventEl.addKeyListener(Ext.EventObject.SPACE, me.selectToday,  me);
        }
        me.update(me.value);
    },

    handleTabClick:function (e) {
        var me = this,
            t = me.getSelectedDate(me.activeDate),
            handler = me.handler;

        
        if (!me.disabled && t.dateValue && !Ext.fly(t.parentNode).hasCls(me.disabledCellCls)) {
            me.doCancelFocus = me.focusOnSelect === false;
            me.setValue(new Date(t.dateValue));
            delete me.doCancelFocus;
            me.fireEvent('select', me, me.value);
            if (handler) {
                handler.call(me.scope || me, me, me.value);
            }
            me.onSelect();
        }
    },

    getSelectedDate:function (date) {
        var me = this,
            t = date.getTime(),
            cells = me.cells,
            cls = me.selectedCls,
            cellItems = cells.elements,
            c,
            cLen = cellItems.length,
            cell;

        cells.removeCls(cls);

        for (c = 0; c < cLen; c++) {
            cell = Ext.fly(cellItems[c]);

            if (cell.dom.firstChild.dateValue == t) {
                return cell.dom.firstChild;
            }
        }
        return null;
    },

    
    initDisabledDays : function(){
        var me = this,
            dd = me.disabledDates,
            re = '(?:',
            len,
            d, dLen, dI;

        if(!me.disabledDatesRE && dd){
                len = dd.length - 1;

            dLen = dd.length;

            for (d = 0; d < dLen; d++) {
                dI = dd[d];

                re += Ext.isDate(dI) ? '^' + Ext.String.escapeRegex(Ext.Date.dateFormat(dI, me.format)) + '$' : dI;
                if (d != len) {
                    re += '|';
                }
            }

            me.disabledDatesRE = new RegExp(re + ')');
        }
    },

    
    setDisabledDates : function(dd){
        var me = this;

        if(Ext.isArray(dd)){
            me.disabledDates = dd;
            me.disabledDatesRE = null;
        }else{
            me.disabledDatesRE = dd;
        }
        me.initDisabledDays();
        me.update(me.value, true);
        return me;
    },

    
    setDisabledDays : function(dd){
        this.disabledDays = dd;
        return this.update(this.value, true);
    },

    
    setMinDate : function(dt){
        this.minDate = dt;
        return this.update(this.value, true);
    },

    
    setMaxDate : function(dt){
        this.maxDate = dt;
        return this.update(this.value, true);
    },

    
    setValue : function(value){
        this.value = Ext.Date.clearTime(value, true);
        return this.update(this.value);
    },

    
    getValue : function(){
        return this.value;
    },

    
    
    getDayInitial: function(value){
        return value.substr(0,1);
    },
    

    
    focus : function(){
        this.update(this.activeDate);
    },

    
    
    onEnable: function(){
        this.callParent();
        this.setDisabledStatus(false);
        this.update(this.activeDate);

    },

    
    
    onDisable : function(){
        this.callParent();
        this.setDisabledStatus(true);
    },

    
    setDisabledStatus : function(disabled){
        var me = this;

        me.keyNav.setDisabled(disabled);
        me.prevRepeater.setDisabled(disabled);
        me.nextRepeater.setDisabled(disabled);
        if (me.showToday) {
            me.todayKeyListener.setDisabled(disabled);
            me.todayBtn.setDisabled(disabled);
        }
    },

    
    getActive: function(){
        return this.activeDate || this.value;
    },

    
    runAnimation: function(isHide){
        var picker = this.monthPicker,
            options = {
                duration: 200,
                callback: function(){
                    if (isHide) {
                        picker.hide();
                    } else {
                        picker.show();
                    }
                }
            };

        if (isHide) {
            picker.el.slideOut('t', options);
        } else {
            picker.el.slideIn('t', options);
        }
    },

    
    hideMonthPicker : function(animate){
        var me = this,
            picker = me.monthPicker;

        if (picker) {
            if (me.shouldAnimate(animate)) {
                me.runAnimation(true);
            } else {
                picker.hide();
            }
        }
        return me;
    },
    
    doShowMonthPicker: function(){
        
        
        this.showMonthPicker();
    },

    
    showMonthPicker : function(animate){
        var me = this,
            el = me.el,
            picker;
        
        if (me.rendered && !me.disabled) {
            picker = me.createMonthPicker();
            picker.setValue(me.getActive());
            picker.setSize(el.getSize());
            picker.setPosition(-el.getBorderWidth('l'), -el.getBorderWidth('t'));
            if (me.shouldAnimate(animate)) {
                me.runAnimation(false);
            } else {
                picker.show();
            }
        }
        return me;
    },
    
    
    shouldAnimate: function(animate){
        return Ext.isDefined(animate) ? animate : !this.disableAnim;
    },

    
    createMonthPicker: function(){
        var me = this,
            picker = me.monthPicker;

        if (!picker) {
            me.monthPicker = picker = new Ext.picker.Month({
                renderTo: me.el,
                floating: true,
                padding: me.padding,
                shadow: false,
                small: me.showToday === false,
                listeners: {
                    scope: me,
                    cancelclick: me.onCancelClick,
                    okclick: me.onOkClick,
                    yeardblclick: me.onOkClick,
                    monthdblclick: me.onOkClick
                }
            });
            if (!me.disableAnim) {
                
                picker.el.setStyle('display', 'none');
            }
            me.on('beforehide', Ext.Function.bind(me.hideMonthPicker, me, [false]));
        }
        return picker;
    },

    
    onOkClick: function(picker, value){
        var me = this,
            month = value[0],
            year = value[1],
            date = new Date(year, month, me.getActive().getDate());

        if (date.getMonth() !== month) {
            
            date = Ext.Date.getLastDateOfMonth(new Date(year, month, 1));
        }
        me.setValue(date);
        me.hideMonthPicker();
    },

    
    onCancelClick: function(){
        
        this.selectedUpdate(this.activeDate);
        this.hideMonthPicker();
    },

    
    showPrevMonth : function(e){
        return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.MONTH, -1));
    },

    
    showNextMonth : function(e){
        return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.MONTH, 1));
    },

    
    showPrevYear : function(){
        return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.YEAR, -1));
    },

    
    showNextYear : function(){
        return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.YEAR, 1));
    },

    
    handleMouseWheel : function(e){
        e.stopEvent();
        if(!this.disabled){
            var delta = e.getWheelDelta();
            if(delta > 0){
                this.showPrevMonth();
            } else if(delta < 0){
                this.showNextMonth();
            }
        }
    },

    
    handleDateClick : function(e, t){
        var me = this,
            handler = me.handler;

        e.stopEvent();
        if(!me.disabled && t.dateValue && !Ext.fly(t.parentNode).hasCls(me.disabledCellCls)){
            me.doCancelFocus = me.focusOnSelect === false;
            me.setValue(new Date(t.dateValue));
            delete me.doCancelFocus;
            me.fireEvent('select', me, me.value);
            if (handler) {
                handler.call(me.scope || me, me, me.value);
            }
            
            
            
            
            me.onSelect();
        }
    },

    
    onSelect: function() {
        if (this.hideOnSelect) {
             this.hide();
         }
    },

    
    selectToday : function(){
        var me = this,
            btn = me.todayBtn,
            handler = me.handler;

        if(btn && !btn.disabled){
            me.setValue(Ext.Date.clearTime(new Date()));
            me.fireEvent('select', me, me.value);
            if (handler) {
                handler.call(me.scope || me, me, me.value);
            }
            me.onSelect();
        }
        return me;
    },

    
    selectedUpdate: function(date){
        var me        = this,
            t         = date.getTime(),
            cells     = me.cells,
            cls       = me.selectedCls,
            cellItems = cells.elements,
            c,
            cLen      = cellItems.length,
            cell;

        cells.removeCls(cls);

        for (c = 0; c < cLen; c++) {
            cell = Ext.fly(cellItems[c]);

            if (cell.dom.firstChild.dateValue == t) {
                me.fireEvent('highlightitem', me, cell);
                cell.addCls(cls);

                if(me.isVisible() && !me.doCancelFocus){
                    Ext.fly(cell.dom.firstChild).focus(50);
                }

                break;
            }
        }
    },

    
    fullUpdate: function(date){
        var me = this,
            cells = me.cells.elements,
            textNodes = me.textNodes,
            disabledCls = me.disabledCellCls,
            eDate = Ext.Date,
            i = 0,
            extraDays = 0,
            visible = me.isVisible(),
            newDate = +eDate.clearTime(date, true),
            today = +eDate.clearTime(new Date()),
            min = me.minDate ? eDate.clearTime(me.minDate, true) : Number.NEGATIVE_INFINITY,
            max = me.maxDate ? eDate.clearTime(me.maxDate, true) : Number.POSITIVE_INFINITY,
            ddMatch = me.disabledDatesRE,
            ddText = me.disabledDatesText,
            ddays = me.disabledDays ? me.disabledDays.join('') : false,
            ddaysText = me.disabledDaysText,
            format = me.format,
            days = eDate.getDaysInMonth(date),
            firstOfMonth = eDate.getFirstDateOfMonth(date),
            startingPos = firstOfMonth.getDay() - me.startDay,
            previousMonth = eDate.add(date, eDate.MONTH, -1),
            longDayFormat = me.longDayFormat,
            prevStart,
            current,
            disableToday,
            tempDate,
            setCellClass,
            html,
            cls,
            formatValue,
            value;

        if (startingPos < 0) {
            startingPos += 7;
        }

        days += startingPos;
        prevStart = eDate.getDaysInMonth(previousMonth) - startingPos;
        current = new Date(previousMonth.getFullYear(), previousMonth.getMonth(), prevStart, me.initHour);

        if (me.showToday) {
            tempDate = eDate.clearTime(new Date());
            disableToday = (tempDate < min || tempDate > max ||
                (ddMatch && format && ddMatch.test(eDate.dateFormat(tempDate, format))) ||
                (ddays && ddays.indexOf(tempDate.getDay()) != -1));

            if (!me.disabled) {
                me.todayBtn.setDisabled(disableToday);
                me.todayKeyListener.setDisabled(disableToday);
            }
        }

        setCellClass = function(cell, cls){
            value = +eDate.clearTime(current, true);
            cell.title = eDate.format(current, longDayFormat);
            
            cell.firstChild.dateValue = value;
            if(value == today){
                cls += ' ' + me.todayCls;
                cell.title = me.todayText;
                
                
                me.todayElSpan = Ext.DomHelper.append(cell.firstChild, {
                    tag:'span',
                    cls: Ext.baseCSSPrefix + 'hide-clip',
                    html:me.todayText
                }, true);
            }
            if(value == newDate) {
                cls += ' ' + me.selectedCls;
                me.fireEvent('highlightitem', me, cell);
                if (visible && me.floating) {
                    Ext.fly(cell.firstChild).focus(50);
                }
            }

            if (value < min) {
                cls += ' ' + disabledCls;
                cell.title = me.minText;
            }
            else if (value > max) {
                cls += ' ' + disabledCls;
                cell.title = me.maxText;
            }
            else if (ddays && ddays.indexOf(current.getDay()) !== -1){
                cell.title = ddaysText;
                cls += ' ' + disabledCls;
            }
            else if (ddMatch && format){
                formatValue = eDate.dateFormat(current, format);
                if(ddMatch.test(formatValue)){
                    cell.title = ddText.replace('%0', formatValue);
                    cls += ' ' + disabledCls;
                }
            }
            cell.className = cls + ' ' + me.cellCls;
        };

        for(; i < me.numDays; ++i) {
            if (i < startingPos) {
                html = (++prevStart);
                cls = me.prevCls;
            } else if (i >= days) {
                html = (++extraDays);
                cls = me.nextCls;
            } else {
                html = i - startingPos + 1;
                cls = me.activeCls;
            }
            textNodes[i].innerHTML = html;
            current.setDate(current.getDate() + 1);
            setCellClass(cells[i], cls);
        }

        me.monthBtn.setText(Ext.Date.format(date, me.monthYearFormat));
    },

    
    update : function(date, forceRefresh){
        var me = this,
            active = me.activeDate;

        if (me.rendered) {
            me.activeDate = date;
            if(!forceRefresh && active && me.el && active.getMonth() == date.getMonth() && active.getFullYear() == date.getFullYear()){
                me.selectedUpdate(date, active);
            } else {
                me.fullUpdate(date, active);
            }
        }
        return me;
    },

    
    
    beforeDestroy : function() {
        var me = this;

        if (me.rendered) {
            Ext.destroy(
                me.todayKeyListener,
                me.keyNav,
                me.monthPicker,
                me.monthBtn,
                me.nextRepeater,
                me.prevRepeater,
                me.todayBtn
            );
            delete me.textNodes;
            delete me.cells.elements;
        }
        me.callParent();
    },

    
    
    onShow: function() {
        this.callParent(arguments);
        if (this.focusOnShow) {
            this.focus();
        }
    }
});


Ext.define('Ext.form.field.Date', {
    extend: Ext.form.field.Picker ,
    alias: 'widget.datefield',
                                  
    alternateClassName: ['Ext.form.DateField', 'Ext.form.Date'],

    
    
    format : "m/d/Y",
    
    
    
    altFormats : "m/d/Y|n/j/Y|n/j/y|m/j/y|n/d/y|m/j/Y|n/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d|Y-m-d|n-j|n/j",
    
    
    
    disabledDaysText : "Disabled",
    
    
    
    disabledDatesText : "Disabled",
    
    
    
    minText : "The date in this field must be equal to or after {0}",
    
    
    
    maxText : "The date in this field must be equal to or before {0}",
    
    
    
    invalidText : "{0} is not a valid date - it must be in the format {1}",
    
    
    triggerCls : Ext.baseCSSPrefix + 'form-date-trigger',
    
    showToday : true,
    
    
    
    

    
    
    
    useStrict: undefined,

    
    
    initTime: '12', 

    initTimeFormat: 'H',

    matchFieldWidth: false,
    
    
    startDay: 0,
    

    initComponent : function(){
        var me = this,
            isString = Ext.isString,
            min, max;

        min = me.minValue;
        max = me.maxValue;
        if(isString(min)){
            me.minValue = me.parseDate(min);
        }
        if(isString(max)){
            me.maxValue = me.parseDate(max);
        }
        me.disabledDatesRE = null;
        me.initDisabledDays();

        me.callParent();
    },

    initValue: function() {
        var me = this,
            value = me.value;

        
        if (Ext.isString(value)) {
            me.value = me.rawToValue(value);
        }

        me.callParent();
    },

    
    initDisabledDays : function(){
        if(this.disabledDates){
            var dd   = this.disabledDates,
                len  = dd.length - 1,
                re   = "(?:",
                d,
                dLen = dd.length,
                date;

            for (d = 0; d < dLen; d++) {
                date = dd[d];

                re += Ext.isDate(date) ? '^' + Ext.String.escapeRegex(date.dateFormat(this.format)) + '$' : date;
                if (d !== len) {
                    re += '|';
                }
            }

            this.disabledDatesRE = new RegExp(re + ')');
        }
    },

    
    setDisabledDates : function(dd){
        var me = this,
            picker = me.picker;

        me.disabledDates = dd;
        me.initDisabledDays();
        if (picker) {
            picker.setDisabledDates(me.disabledDatesRE);
        }
    },

    
    setDisabledDays : function(dd){
        var picker = this.picker;

        this.disabledDays = dd;
        if (picker) {
            picker.setDisabledDays(dd);
        }
    },

    
    setMinValue : function(dt){
        var me = this,
            picker = me.picker,
            minValue = (Ext.isString(dt) ? me.parseDate(dt) : dt);

        me.minValue = minValue;
        if (picker) {
            picker.minText = Ext.String.format(me.minText, me.formatDate(me.minValue));
            picker.setMinDate(minValue);
        }
    },

    
    setMaxValue : function(dt){
        var me = this,
            picker = me.picker,
            maxValue = (Ext.isString(dt) ? me.parseDate(dt) : dt);

        me.maxValue = maxValue;
        if (picker) {
            picker.maxText = Ext.String.format(me.maxText, me.formatDate(me.maxValue));
            picker.setMaxDate(maxValue);
        }
    },

    
    getErrors: function(value) {
        var me = this,
            format = Ext.String.format,
            clearTime = Ext.Date.clearTime,
            errors = me.callParent(arguments),
            disabledDays = me.disabledDays,
            disabledDatesRE = me.disabledDatesRE,
            minValue = me.minValue,
            maxValue = me.maxValue,
            len = disabledDays ? disabledDays.length : 0,
            i = 0,
            svalue,
            fvalue,
            day,
            time;

        value = me.formatDate(value || me.processRawValue(me.getRawValue()));

        if (value === null || value.length < 1) { 
             return errors;
        }

        svalue = value;
        value = me.parseDate(value);
        if (!value) {
            errors.push(format(me.invalidText, svalue, Ext.Date.unescapeFormat(me.format)));
            return errors;
        }

        time = value.getTime();
        if (minValue && time < clearTime(minValue).getTime()) {
            errors.push(format(me.minText, me.formatDate(minValue)));
        }

        if (maxValue && time > clearTime(maxValue).getTime()) {
            errors.push(format(me.maxText, me.formatDate(maxValue)));
        }

        if (disabledDays) {
            day = value.getDay();

            for(; i < len; i++) {
                if (day === disabledDays[i]) {
                    errors.push(me.disabledDaysText);
                    break;
                }
            }
        }

        fvalue = me.formatDate(value);
        if (disabledDatesRE && disabledDatesRE.test(fvalue)) {
            errors.push(format(me.disabledDatesText, fvalue));
        }

        return errors;
    },

    rawToValue: function(rawValue) {
        return this.parseDate(rawValue) || rawValue || null;
    },

    valueToRaw: function(value) {
        return this.formatDate(this.parseDate(value));
    },

    

    
    safeParse : function(value, format) {
        var me = this,
            utilDate = Ext.Date,
            result = null,
            strict = me.useStrict,
            parsedDate;

        if (utilDate.formatContainsHourInfo(format)) {
            
            result = utilDate.parse(value, format, strict);
        } else {
            
            parsedDate = utilDate.parse(value + ' ' + me.initTime, format + ' ' + me.initTimeFormat, strict);
            if (parsedDate) {
                result = utilDate.clearTime(parsedDate);
            }
        }
        return result;
    },

    
    getSubmitValue: function() {
        var format = this.submitFormat || this.format,
            value = this.getValue();

        return value ? Ext.Date.format(value, format) : '';
    },

    
    parseDate : function(value) {
        if(!value || Ext.isDate(value)){
            return value;
        }

        var me = this,
            val = me.safeParse(value, me.format),
            altFormats = me.altFormats,
            altFormatsArray = me.altFormatsArray,
            i = 0,
            len;

        if (!val && altFormats) {
            altFormatsArray = altFormatsArray || altFormats.split('|');
            len = altFormatsArray.length;
            for (; i < len && !val; ++i) {
                val = me.safeParse(value, altFormatsArray[i]);
            }
        }
        return val;
    },

    
    formatDate : function(date){
        return Ext.isDate(date) ? Ext.Date.dateFormat(date, this.format) : date;
    },

    createPicker: function() {
        var me = this,
            format = Ext.String.format;

        return new Ext.picker.Date({
            pickerField: me,
            ownerCt: me.ownerCt,
            renderTo: document.body,
            floating: true,
            hidden: true,
            focusOnShow: true,
            minDate: me.minValue,
            maxDate: me.maxValue,
            disabledDatesRE: me.disabledDatesRE,
            disabledDatesText: me.disabledDatesText,
            disabledDays: me.disabledDays,
            disabledDaysText: me.disabledDaysText,
            format: me.format,
            showToday: me.showToday,
            startDay: me.startDay,
            minText: format(me.minText, me.formatDate(me.minValue)),
            maxText: format(me.maxText, me.formatDate(me.maxValue)),
            listeners: {
                scope: me,
                select: me.onSelect
            },
            keyNavConfig: {
                esc: function() {
                    me.collapse();
                }
            }
        });
    },
    
    onDownArrow: function(e) {
        this.callParent(arguments);
        if (this.isExpanded) {
            this.getPicker().focus();
        }
    },

    onSelect: function(m, d) {
        var me = this;

        me.setValue(d);
        me.fireEvent('select', me, d);
        me.collapse();
    },

    
    onExpand: function() {
        var value = this.getValue();
        this.picker.setValue(Ext.isDate(value) ? value : new Date());
    },

    
    onCollapse: function() {
        this.focus(false, 60);
    },

    
    beforeBlur : function(){
        var me = this,
            v = me.parseDate(me.getRawValue()),
            focusTask = me.focusTask;

        if (focusTask) {
            focusTask.cancel();
        }

        if (v) {
            me.setValue(v);
        }
    }

    
    
    
    
});


Ext.define('Ext.form.field.FileButton', {
    extend:  Ext.button.Button ,
    alias: 'widget.filebutton',
    
    childEls: [
        'btnEl', 'btnWrap', 'btnInnerEl', 'btnIconEl', 'fileInputEl'
    ],
    
    inputCls: Ext.baseCSSPrefix + 'form-file-input',
    
    cls: Ext.baseCSSPrefix + 'form-file-btn',
    
    preventDefault: false,

    renderTpl: [
        '<span id="{id}-btnWrap" role="presentation" class="{baseCls}-wrap',
            '<tpl if="splitCls"> {splitCls}</tpl>',
            '{childElCls}" unselectable="on">',
            '<span id="{id}-btnEl" class="{baseCls}-button" role="button">',
                '<span id="{id}-btnInnerEl" class="{baseCls}-inner {innerCls}',
                    '{childElCls}" unselectable="on">',
                    '{text}',
                '</span>',
                '<span role="presentation" id="{id}-btnIconEl" class="{baseCls}-icon-el {iconCls}',
                    '{childElCls} {glyphCls}" unselectable="on" style="',
                    '<tpl if="iconUrl">background-image:url({iconUrl});</tpl>',
                    '<tpl if="glyph && glyphFontFamily">font-family:{glyphFontFamily};</tpl>">',
                    '<tpl if="glyph">&#{glyph};</tpl><tpl if="iconCls || iconUrl">&#160;</tpl>',
                '</span>',
            '</span>',
        '</span>',
        '<input id="{id}-fileInputEl" class="{childElCls} {inputCls}" type="file" size="1" name="{inputName}" role="{role}">'
    ],
    
    getTemplateArgs: function(){
        var args = this.callParent();
        args.inputCls = this.inputCls;
        args.inputName = this.inputName;
        return args;
    },
    
    afterRender: function(){
        var me = this;
        me.callParent(arguments);
        me.fileInputEl.on('change', me.fireChange, me);    
    },
    
    fireChange: function(e){
        this.fireEvent('change', this, e, this.fileInputEl.dom.value);
    },
    
    
    createFileInput : function(isTemporary) {
        var me = this;
        me.fileInputEl = me.el.createChild({
            name: me.inputName,
            id: !isTemporary ? me.id + '-fileInputEl' : undefined,
            cls: me.inputCls,
            tag: 'input',
            type: 'file',
            size: 1
        });
        me.fileInputEl.on('change', me.fireChange, me);  
    },
    
    reset: function(remove){
        if (remove) {
            this.fileInputEl.remove();
        }
        this.createFileInput(!remove);
    },
    
    restoreInput: function(el){
        this.fileInputEl.remove();
        el = Ext.get(el);
        this.el.appendChild(el);
        this.fileInputEl = el;
    },
    
    onDisable: function(){
        this.callParent();
        this.fileInputEl.dom.disabled = true;
    },

    onEnable : function() {
        this.callParent();
        this.fileInputEl.dom.disabled = false;
    }
});


Ext.define('Ext.form.field.File', {
    extend:  Ext.form.field.Trigger ,
    alias: ['widget.filefield', 'widget.fileuploadfield'],
    alternateClassName: ['Ext.form.FileUploadField', 'Ext.ux.form.FileUploadField', 'Ext.form.File'],
               
                                   
      

    
    
    buttonText: 'Browse...',
    

    
    buttonOnly: false,

    
    buttonMargin: 3,
    
    
    clearOnSubmit: true,

    

    

    

    


    
    extraFieldBodyCls: Ext.baseCSSPrefix + 'form-file-wrap',

    
    readOnly: true,

    submitValue: false,

    
    triggerNoEditCls: '',

    
    componentLayout: 'triggerfield',

    
    childEls: ['browseButtonWrap'],

    
    onRender: function() {
        var me = this,
            id = me.id,
            inputEl;

        me.callParent(arguments);

        inputEl = me.inputEl;
        inputEl.dom.name = ''; 

        
        
        
        me.button = new Ext.form.field.FileButton(Ext.apply({
            renderTo: id + '-browseButtonWrap',
            ownerCt: me,
            ownerLayout: me.componentLayout,
            id: id + '-button',
            ui: me.ui,
            disabled: me.disabled,
            text: me.buttonText,
            style: me.buttonOnly ? '' : me.getButtonMarginProp() + me.buttonMargin + 'px',
            inputName: me.getName(),
            listeners: {
                scope: me,
                change: me.onFileChange
            }
        }, me.buttonConfig));
        me.fileInputEl = me.button.fileInputEl;

        if (me.buttonOnly) {
            me.inputCell.setDisplayed(false);
        }

        
        me.browseButtonWrap.dom.style.width = (me.browseButtonWrap.dom.lastChild.offsetWidth + me.button.getEl().getMargin('lr')) + 'px';
        if (Ext.isIE) {
            me.button.getEl().repaint();
        }
    },

    
    getTriggerMarkup: function() {
        return '<td id="' + this.id + '-browseButtonWrap" role="presentation"></td>';
    },

    
    onFileChange: function(button, e, value) {
        this.duringFileSelect = true;
        Ext.form.field.File.superclass.setValue.call(this, value);
        delete this.duringFileSelect;
    },
    
    didValueChange: function(){
        
        
        
        
        return !!this.duringFileSelect;
    },

    
    setValue: Ext.emptyFn,

    reset : function(){
        var me = this,
            clear = me.clearOnSubmit;
        if (me.rendered) {
            me.button.reset(clear);
            me.fileInputEl = me.button.fileInputEl;
            if (clear) {
                me.inputEl.dom.value = '';
                
                Ext.form.field.File.superclass.setValue.call(this, null);
            }
        }
        me.callParent();
    },
    
    onShow: function(){
        this.callParent();
        
        
        this.button.updateLayout();    
    },

    onDisable: function(){
        this.callParent();
        this.button.disable();
    },

    onEnable: function(){
        this.callParent();
        this.button.enable();
    },

    isFileUpload: function() {
        return true;
    },

    extractFileInput: function() {
        var me = this,
            fileInput;
            
        if (me.rendered) {
            fileInput = me.button.fileInputEl.dom;
            me.reset();
        } else {
            
            
            fileInput = document.createElement('input');
            fileInput.type = 'file';
            fileInput.className = Ext.baseCSSPrefix + 'hide-display';
            fileInput.name = me.getName();
        }
        return fileInput;
    },
    
    restoreInput: function(el) {
        
        
        if (this.rendered) {
            var button = this.button;
            button.restoreInput(el);
            this.fileInputEl = button.fileInputEl;
        }
    },

    onDestroy: function(){
        Ext.destroyMembers(this, 'button');
        delete this.fileInputEl;
        this.callParent();
    },

    getButtonMarginProp: function() {
        return 'margin-left:';
    }
});


Ext.define('Ext.form.field.Hidden', {
    extend: Ext.form.field.Base ,
    alias: ['widget.hiddenfield', 'widget.hidden'],
    alternateClassName: 'Ext.form.Hidden',

    
    inputType : 'hidden',
    hideLabel: true,
    hidden: true,
    
    ariaRole: 'presentation',
    
    initComponent: function() {
        this.formItemCls += '-hidden';
        this.callParent();    
    },
    
    
    isEqual: function(value1, value2) {
        return this.isEqualAsString(value1, value2);
    },

    
    initEvents: Ext.emptyFn,
    setSize : Ext.emptyFn,
    setWidth : Ext.emptyFn,
    setHeight : Ext.emptyFn,
    setPosition : Ext.emptyFn,
    setPagePosition : Ext.emptyFn,
    markInvalid : Ext.emptyFn,
    clearInvalid : Ext.emptyFn
});


Ext.define('Ext.picker.Color', {
    extend:  Ext.Component ,
                              
    alias: 'widget.colorpicker',
    alternateClassName: 'Ext.ColorPalette',

    
    componentCls : Ext.baseCSSPrefix + 'color-picker',

    
    selectedCls: Ext.baseCSSPrefix + 'color-picker-selected',

    
    itemCls: Ext.baseCSSPrefix + 'color-picker-item',

    
    value : null,

    
    clickEvent :'click',

    
    allowReselect : false,

    
    colors : [
        '000000', '993300', '333300', '003300', '003366', '000080', '333399', '333333',
        '800000', 'FF6600', '808000', '008000', '008080', '0000FF', '666699', '808080',
        'FF0000', 'FF9900', '99CC00', '339966', '33CCCC', '3366FF', '800080', '969696',
        'FF00FF', 'FFCC00', 'FFFF00', '00FF00', '00FFFF', '00CCFF', '993366', 'C0C0C0',
        'FF99CC', 'FFCC99', 'FFFF99', 'CCFFCC', 'CCFFFF', '99CCFF', 'CC99FF', 'FFFFFF'
    ],

    

    

    colorRe: /(?:^|\s)color-(.{6})(?:\s|$)/,
    
    renderTpl: [
        '<tpl for="colors">',
            '<a href="#" role="button" class="color-{.} {parent.itemCls}" hidefocus="on">',
                '<span class="{parent.itemCls}-inner" style="background:#{.}">&#160;</span>',
            '</a>',
        '</tpl>'
    ],

    
    initComponent : function(){
        var me = this;

        me.callParent(arguments);
        me.addEvents(
            
            'select'
        );

        if (me.handler) {
            me.on('select', me.handler, me.scope, true);
        }
    },


    
    initRenderData : function(){
        var me = this;
        return Ext.apply(me.callParent(), {
            itemCls: me.itemCls,
            colors: me.colors
        });
    },

    onRender : function(){
        var me = this,
            clickEvent = me.clickEvent;

        me.callParent(arguments);

        me.mon(me.el, clickEvent, me.handleClick, me, {delegate: 'a'});
        
        if(clickEvent != 'click'){
            me.mon(me.el, 'click', Ext.emptyFn, me, {delegate: 'a', stopEvent: true});
        }
    },

    
    afterRender : function(){
        var me = this,
            value;

        me.callParent(arguments);
        if (me.value) {
            value = me.value;
            me.value = null;
            me.select(value, true);
        }
    },

    
    handleClick : function(event, target){
        var me = this,
            color;

        event.stopEvent();
        if (!me.disabled) {
            color = target.className.match(me.colorRe)[1];
            me.select(color.toUpperCase());
        }
    },

    
    select : function(color, suppressEvent){

        var me = this,
            selectedCls = me.selectedCls,
            value = me.value,
            el;

        color = color.replace('#', '');
        if (!me.rendered) {
            me.value = color;
            return;
        }


        if (color != value || me.allowReselect) {
            el = me.el;

            if (me.value) {
                el.down('a.color-' + value).removeCls(selectedCls);
            }
            el.down('a.color-' + color).addCls(selectedCls);
            me.value = color;
            if (suppressEvent !== true) {
                me.fireEvent('select', me, color);
            }
        }
    },
    
    
    clear: function(){
        var me = this,
            value = me.value,
            el;
            
        if (value && me.rendered) {
            el = me.el.down('a.color-' + value);
            el.removeCls(me.selectedCls);
        }
        me.value = null;  
    },

    
    getValue: function(){
        return this.value || null;
    }
});


Ext.define('Ext.layout.component.field.HtmlEditor', {
    extend:  Ext.layout.component.field.FieldContainer ,
    alias: ['layout.htmleditor'],

    type: 'htmleditor',
    
    naturalHeight: 150,
    naturalWidth: 300,

    beginLayout: function(ownerContext) {
        var owner = this.owner,
            dom;
            
        
        
        
        if (Ext.isGecko) {
            dom = owner.textareaEl.dom;
            this.lastValue = dom.value;
            dom.value = '';
        }
        this.callParent(arguments);
        
        ownerContext.toolbarContext  = ownerContext.context.getCmp(owner.toolbar);
        ownerContext.inputCmpContext = ownerContext.context.getCmp(owner.inputCmp);
        ownerContext.textAreaContext = ownerContext.getEl('textareaEl');
        ownerContext.iframeContext   = ownerContext.getEl('iframeEl');
    },
    
    beginLayoutCycle: function(ownerContext) {
        var me = this,
            widthModel = ownerContext.widthModel,
            heightModel = ownerContext.heightModel,
            owner = me.owner,
            iframeEl = owner.iframeEl,
            textareaEl = owner.textareaEl;
            
        me.callParent(arguments);
        if (widthModel.shrinkWrap) {
            iframeEl.setStyle('width', '');
            textareaEl.setStyle('width', '');
        } else if (widthModel.natural) {
            ownerContext.bodyCellContext.setWidth(me.naturalWidth);
        }
        
        if (heightModel.natural || heightModel.shrinkWrap) {
            iframeEl.setHeight(me.naturalHeight);
            textareaEl.setHeight(me.naturalHeight);
        }
    },
    
    finishedLayout: function(){
        var owner = this.owner;
        
        this.callParent(arguments);
        
        
        if (Ext.isIE9m && Ext.isIEQuirks) {
            owner.el.repaint();
        }    
        if (Ext.isGecko) {
            owner.textareaEl.dom.value = this.lastValue;
        }
    },
    
    publishOwnerWidth: function(ownerContext, width){
        this.callParent(arguments);
        width -= ownerContext.inputCmpContext.getBorderInfo().width;
        ownerContext.textAreaContext.setWidth(width);
        ownerContext.iframeContext.setWidth(width);
    },
    
    
    
    
    
    
    
    publishInnerWidth: function(ownerContext, width){
        var border = ownerContext.inputCmpContext.getBorderInfo().width,
            ieBug = Ext.isStrict && Ext.isIE8m,
            natural = ownerContext.widthModel.natural;
          
        this.callParent(arguments);
        width = ownerContext.bodyCellContext.props.width - border;
        if (natural) {
            if (ieBug) {
                width -= 2;
            }
            ownerContext.textAreaContext.setWidth(width);
            ownerContext.iframeContext.setWidth(width);
        } else if (ieBug) {
            ownerContext.textAreaContext.setWidth(width);
        }
    },

    publishInnerHeight: function (ownerContext, height) {
        var toolbarHeight = ownerContext.toolbarContext.getProp('height'),
            sourceEdit = this.owner.sourceEditMode;
        
        this.callParent(arguments);
        height = ownerContext.bodyCellContext.props.height;
        
        if (toolbarHeight !== undefined) {
            height -= toolbarHeight + ownerContext.inputCmpContext.getFrameInfo().height;
            if (Ext.isIE8 && Ext.isStrict) {
                height -= 2;
            } else if (Ext.isIEQuirks && (Ext.isIE8 || Ext.isIE9)) {
                height -= 4;
            }
            ownerContext.iframeContext.setHeight(height);    
            ownerContext.textAreaContext.setHeight(height);    
        } else {
            this.done = false;
        }
    }
 
});


Ext.define('Ext.form.field.HtmlEditor', {
    extend:  Ext.form.FieldContainer ,
    mixins: {
        field:  Ext.form.field.Field 
    },
    alias: 'widget.htmleditor',
    alternateClassName: 'Ext.form.HtmlEditor',
               
                                  
                           
                                    
                           
                              
                          
                                               
      
    
    componentLayout: 'htmleditor',

    componentTpl: [
        '{beforeTextAreaTpl}',
        '<textarea id="{id}-textareaEl" name="{name}" tabIndex="-1" {inputAttrTpl}',
                 ' class="{textareaCls}" autocomplete="off">',
            '{[Ext.util.Format.htmlEncode(values.value)]}',
        '</textarea>',
        '{afterTextAreaTpl}',
        '{beforeIFrameTpl}',
        '<iframe id="{id}-iframeEl" name="{iframeName}" frameBorder="0" {iframeAttrTpl}',
               ' src="{iframeSrc}" class="{iframeCls}"></iframe>',
        '{afterIFrameTpl}',
        {
            disableFormats: true
        }
    ],
    
    stretchInputElFixed: true,

    subTplInsertions: [
        
        'beforeTextAreaTpl',

        
        'afterTextAreaTpl',

        
        'beforeIFrameTpl',

        
        'afterIFrameTpl',

        
        'iframeAttrTpl',

        
        'inputAttrTpl'
    ],

    
    enableFormat: true,
    
    enableFontSize: true,
    
    enableColors: true,
    
    enableAlignments: true,
    
    enableLists: true,
    
    enableSourceEdit: true,
    
    enableLinks: true,
    
    enableFont: true,
    
    
    createLinkText: 'Please enter the URL for the link:',
    
    
    defaultLinkValue: 'http:/'+'/',
    
    fontFamilies: [
        'Arial',
        'Courier New',
        'Tahoma',
        'Times New Roman',
        'Verdana'
    ],
    
    defaultValue: (Ext.isOpera || Ext.isIE6) ? '&#160;' : '&#8203;',

    
    extraFieldBodyCls: Ext.baseCSSPrefix + 'html-editor-wrap',

    

    
    initialized: false,
    
    activated: false,
    
    sourceEditMode: false,
    
    iframePad:3,
    
    hideMode:'offsets',

    maskOnDisable: true,

    containerElCls: Ext.baseCSSPrefix + 'html-editor-container',

    
    reStripQuotes: /^['"]*|['"]*$/g,

    // @private
    initComponent: function(){
        var me = this;

        me.addEvents(
            /**
             * @event initialize
             * Fires when the editor is fully initialized (including the iframe)
             * @param {Ext.form.field.HtmlEditor} this
             */
            'initialize',
            /**
             * @event activate
             * Fires when the editor is first receives the focus. Any insertion must wait until after this event.
             * @param {Ext.form.field.HtmlEditor} this
             */
            'activate',
             /**
             * @event beforesync
             * Fires before the textarea is updated with content from the editor iframe. Return false to cancel the
             * sync.
             * @param {Ext.form.field.HtmlEditor} this
             * @param {String} html
             */
            'beforesync',
             /**
             * @event beforepush
             * Fires before the iframe editor is updated with content from the textarea. Return false to cancel the
             * push.
             * @param {Ext.form.field.HtmlEditor} this
             * @param {String} html
             */
            'beforepush',
             /**
             * @event sync
             * Fires when the textarea is updated with content from the editor iframe.
             * @param {Ext.form.field.HtmlEditor} this
             * @param {String} html
             */
            'sync',
             /**
             * @event push
             * Fires when the iframe editor is updated with content from the textarea.
             * @param {Ext.form.field.HtmlEditor} this
             * @param {String} html
             */
            'push',
             /**
             * @event editmodechange
             * Fires when the editor switches edit modes
             * @param {Ext.form.field.HtmlEditor} this
             * @param {Boolean} sourceEdit True if source edit, false if standard editing.
             */
            'editmodechange'
        );

        me.items = [me.createToolbar(), me.createInputCmp()];

        me.layout = {
            type: 'vbox',
            align: 'stretch'
        };

        me.callParent(arguments);
        me.initField();
    },
    
    createInputCmp: function(){
        this.inputCmp = Ext.widget(this.getInputCmpCfg());
        return this.inputCmp;
    },
    
    getInputCmpCfg: function(){
        var me = this,
            id = me.id + '-inputCmp',
            data = {
                id          : id,
                name        : me.name,
                textareaCls : Ext.baseCSSPrefix + 'hidden',
                value       : me.value,
                iframeName  : Ext.id(),
                iframeSrc   : Ext.SSL_SECURE_URL,
                iframeCls   : Ext.baseCSSPrefix + 'htmleditor-iframe'
            };
            
        me.getInsertionRenderData(data, me.subTplInsertions);
            
        return {
            flex: 1,
            xtype: 'component',
            tpl: me.getTpl('componentTpl'),
            childEls: ['iframeEl', 'textareaEl'],
            id: id, 
            cls: Ext.baseCSSPrefix + 'html-editor-input',
            data: data 
        };    
    },

    /*
     * Called when the editor creates its toolbar. Override this method if you need to
     * add custom toolbar buttons.
     * @param {Ext.form.field.HtmlEditor} editor
     * @protected
     */
    createToolbar: function(){
        this.toolbar = Ext.widget(this.getToolbarCfg());
        return this.toolbar;
    },
    
    getToolbarCfg: function(){
        var me = this,
            items = [], i,
            tipsEnabled = Ext.quickTipsActive && Ext.tip.QuickTipManager.isEnabled(),
            baseCSSPrefix = Ext.baseCSSPrefix,
            fontSelectItem, undef;

        function btn(id, toggle, handler){
            return {
                itemId: id,
                cls: baseCSSPrefix + 'btn-icon',
                iconCls: baseCSSPrefix + 'edit-'+id,
                enableToggle:toggle !== false,
                scope: me,
                handler:handler||me.relayBtnCmd,
                clickEvent: 'mousedown',
                tooltip: tipsEnabled ? me.buttonTips[id] || undef : undef,
                overflowText: me.buttonTips[id].title || undef,
                tabIndex: -1
            };
        }


        if (me.enableFont && !Ext.isSafari2) {
            fontSelectItem = Ext.widget('component', {
                itemId: 'fontSelect',
                renderTpl: [
                    '<select id="{id}-selectEl" class="' + baseCSSPrefix + 'font-select">',
                    '</select>'
                ],
                childEls: ['selectEl'],
                afterRender: function() {
                    me.fontSelect = this.selectEl;
                    Ext.Component.prototype.afterRender.apply(this, arguments);
                },
                onDisable: function() {
                    var selectEl = this.selectEl;
                    if (selectEl) {
                        selectEl.dom.disabled = true;
                    }
                    Ext.Component.prototype.onDisable.apply(this, arguments);
                },
                onEnable: function() {
                    var selectEl = this.selectEl;
                    if (selectEl) {
                        selectEl.dom.disabled = false;
                    }
                    Ext.Component.prototype.onEnable.apply(this, arguments);
                },
                listeners: {
                    change: function() {
                        me.win.focus();
                        me.relayCmd('fontName', me.fontSelect.dom.value);
                        me.deferFocus();
                    },
                    element: 'selectEl'
                }
            });

            items.push(
                fontSelectItem,
                '-'
            );
        }

        if (me.enableFormat) {
            items.push(
                btn('bold'),
                btn('italic'),
                btn('underline')
            );
        }

        if (me.enableFontSize) {
            items.push(
                '-',
                btn('increasefontsize', false, me.adjustFont),
                btn('decreasefontsize', false, me.adjustFont)
            );
        }

        if (me.enableColors) {
            items.push(
                '-', {
                    itemId: 'forecolor',
                    cls: baseCSSPrefix + 'btn-icon',
                    iconCls: baseCSSPrefix + 'edit-forecolor',
                    overflowText: me.buttonTips.forecolor.title,
                    tooltip: tipsEnabled ? me.buttonTips.forecolor || undef : undef,
                    tabIndex:-1,
                    menu: Ext.widget('menu', {
                        plain: true,

                        items: [{
                            xtype: 'colorpicker',
                            allowReselect: true,
                            focus: Ext.emptyFn,
                            value: '000000',
                            plain: true,
                            clickEvent: 'mousedown',
                            handler: function(cp, color) {
                                me.relayCmd('forecolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
                                this.up('menu').hide();
                            }
                        }]
                    })
                }, {
                    itemId: 'backcolor',
                    cls: baseCSSPrefix + 'btn-icon',
                    iconCls: baseCSSPrefix + 'edit-backcolor',
                    overflowText: me.buttonTips.backcolor.title,
                    tooltip: tipsEnabled ? me.buttonTips.backcolor || undef : undef,
                    tabIndex:-1,
                    menu: Ext.widget('menu', {
                        plain: true,

                        items: [{
                            xtype: 'colorpicker',
                            focus: Ext.emptyFn,
                            value: 'FFFFFF',
                            plain: true,
                            allowReselect: true,
                            clickEvent: 'mousedown',
                            handler: function(cp, color) {
                                if (Ext.isGecko) {
                                    me.execCmd('useCSS', false);
                                    me.execCmd('hilitecolor', '#'+color);
                                    me.execCmd('useCSS', true);
                                    me.deferFocus();
                                } else {
                                    me.relayCmd(Ext.isOpera ? 'hilitecolor' : 'backcolor', Ext.isWebKit || Ext.isIE || Ext.isOpera ? '#'+color : color);
                                }
                                this.up('menu').hide();
                            }
                        }]
                    })
                }
            );
        }

        if (me.enableAlignments) {
            items.push(
                '-',
                btn('justifyleft'),
                btn('justifycenter'),
                btn('justifyright')
            );
        }

        if (!Ext.isSafari2) {
            if (me.enableLinks) {
                items.push(
                    '-',
                    btn('createlink', false, me.createLink)
                );
            }

            if (me.enableLists) {
                items.push(
                    '-',
                    btn('insertorderedlist'),
                    btn('insertunorderedlist')
                );
            }
            if (me.enableSourceEdit) {
                items.push(
                    '-',
                    btn('sourceedit', true, function(btn){
                        me.toggleSourceEdit(!me.sourceEditMode);
                    })
                );
            }
        }
        
        // Everything starts disabled.
        for (i = 0; i < items.length; i++) {
            if (items[i].itemId !== 'sourceedit') {
                items[i].disabled = true;
            }
        }

        // build the toolbar
        // Automatically rendered in AbstractComponent.afterRender's renderChildren call
        return {
            xtype: 'toolbar',
            defaultButtonUI: me.defaultButtonUI,
            cls: Ext.baseCSSPrefix + 'html-editor-tb',
            enableOverflow: true,
            items: items,

            // stop form submits
            listeners: {
                click: function(e){
                    e.preventDefault();
                },
                element: 'el'
            }
        }; 
    },
    
    getMaskTarget: function(){
        // Can't be the body td directly because of issues with absolute positioning
        // inside td's in FF
        return Ext.isGecko ? this.inputCmp.el : this.bodyEl;
    },

    /**
     * Sets the read only state of this field.
     * @param {Boolean} readOnly Whether the field should be read only.
     */
    setReadOnly: function(readOnly) {
        var me = this,
            textareaEl = me.textareaEl,
            iframeEl = me.iframeEl,
            body;

        me.readOnly = readOnly;

        if (textareaEl) {
            textareaEl.dom.readOnly = readOnly;
        }

        if (me.initialized) {
            body = me.getEditorBody();
            if (Ext.isIE) {
                // Hide the iframe while setting contentEditable so it doesn't grab focus
                iframeEl.setDisplayed(false);
                body.contentEditable = !readOnly;
                iframeEl.setDisplayed(true);
            } else {
                me.setDesignMode(!readOnly);
            }
            if (body) {
                body.style.cursor = readOnly ? 'default' : 'text';
            }
            me.disableItems(readOnly);
        }
    },

    /**
     * Called when the editor initializes the iframe with HTML contents. Override this method if you
     * want to change the initialization markup of the iframe (e.g. to add stylesheets).
     *
     * **Note:** IE8-Standards has unwanted scroller behavior, so the default meta tag forces IE7 compatibility.
     * Also note that forcing IE7 mode works when the page is loaded normally, but if you are using IE's Web
     * Developer Tools to manually set the document mode, that will take precedence and override what this
     * code sets by default. This can be confusing when developing, but is not a user-facing issue.
     * @protected
     */
    getDocMarkup: function() {
        var me = this,
            h = me.iframeEl.getHeight() - me.iframePad * 2,
            oldIE = Ext.isIE8m;

        // - IE9+ require a strict doctype otherwise text outside visible area can't be selected.
        // - Opera inserts <P> tags on Return key, so P margins must be removed to avoid double line-height.
        // - On browsers other than IE, the font is not inherited by the IFRAME so it must be specified.
        return Ext.String.format(
            (oldIE ? '' : '<!DOCTYPE html>')                        
            + '<html><head><style type="text/css">' 
            + (Ext.isOpera ? 'p{margin:0}' : '')
            + 'body{border:0;margin:0;padding:{0}px;direction:' + (me.rtl ? 'rtl;' : 'ltr;')
            + (oldIE ? Ext.emptyString : 'min-')
            + 'height:{1}px;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;cursor:text;background-color:white;' 
            + (Ext.isIE ? '' : 'font-size:12px;font-family:{2}')
            + '}</style></head><body></body></html>'
            , me.iframePad, h, me.defaultFont);
    },

    // @private
    getEditorBody: function() {
        var doc = this.getDoc();
        return doc.body || doc.documentElement;
    },

    // @private
    getDoc: function() {
        return this.iframeEl.dom.contentDocument || this.getWin().document;
    },

    // @private
    getWin: function() {
        // using window.frames[id] to access the the iframe's window object in FF creates
        // a global variable with name == id in the global scope that references the iframe
        // window.  This is undesirable for unit testing because that global variable
        // is readonly and cannot be deleted.  To avoid this, we use contentWindow if it
        // is available (and it is in all supported browsers at the time of this writing)
        // and fall back to window.frames if contentWindow is not available.
        return this.iframeEl.dom.contentWindow || window.frames[this.iframeEl.dom.name];
    },
    
    initDefaultFont: function(){
        // It's not ideal to do this here since it's a write phase, but we need to know
        // what the font used in the textarea is so that we can setup the appropriate font
        // options in the select box. The select box will reflow once we populate it, so we want
        // to do so before we layout the first time.
        
        var me = this,
            selIdx = 0,
            fonts, font, select,
            option, i, len, lower;
        
        if (!me.defaultFont) {
            font = me.textareaEl.getStyle('font-family');
            font = Ext.String.capitalize(font.split(',')[0]);
            fonts = Ext.Array.clone(me.fontFamilies);
            Ext.Array.include(fonts, font);
            fonts.sort();
            me.defaultFont = font;
            
            select = me.down('#fontSelect').selectEl.dom;
            for (i = 0, len = fonts.length; i < len; ++i) {
                font = fonts[i];
                lower = font.toLowerCase();
                option = new Option(font, lower);
                if (font == me.defaultFont) {
                    selIdx = i;
                }
                option.style.fontFamily = lower;
                
                if (Ext.isIE) {
                    select.add(option);
                } else {
                    select.options.add(option); 
                }
            }
            // Old IE versions have a problem if we set the selected property
            // in the loop, so set it after.
            select.options[selIdx].selected = true;
        } 
    },
    
    isEqual: function(value1, value2){
        return this.isEqualAsString(value1, value2);
    },

    // @private
    afterRender: function() {
        var me = this,
            inputCmp = me.inputCmp;

        me.callParent(arguments);
          
        me.iframeEl = inputCmp.iframeEl;
        me.textareaEl = inputCmp.textareaEl;
        
        // The input element is interrogated by the layout to extract height when labelAlign is 'top'
        // It must be set, and then switched between the iframe and the textarea
        me.inputEl = me.iframeEl;

        if (me.enableFont) {        
            me.initDefaultFont();
        }

        // Start polling for when the iframe document is ready to be manipulated
        me.monitorTask = Ext.TaskManager.start({
            run: me.checkDesignMode,
            scope: me,
            interval: 100
        });
    },

    initFrameDoc: function() {
        var me = this,
            doc, task;

        Ext.TaskManager.stop(me.monitorTask);

        doc = me.getDoc();
        me.win = me.getWin();

        doc.open();
        doc.write(me.getDocMarkup());
        doc.close();

        task = { // must defer to wait for browser to be ready
            run: function() {
                var doc = me.getDoc();
                if (doc.body || doc.readyState === 'complete') {
                    Ext.TaskManager.stop(task);
                    me.setDesignMode(true);
                    Ext.defer(me.initEditor, 10, me);
                }
            },
            interval: 10,
            duration:10000,
            scope: me
        };
        Ext.TaskManager.start(task);
    },

    checkDesignMode: function() {
        var me = this,
            doc = me.getDoc();
        if (doc && (!doc.editorInitialized || me.getDesignMode() !== 'on')) {
            me.initFrameDoc();
        }
    },

    /**
     * @private
     * Sets current design mode. To enable, mode can be true or 'on', off otherwise
     */
    setDesignMode: function(mode) {
        var me = this,
            doc = me.getDoc();
        if (doc) {
            if (me.readOnly) {
                mode = false;
            }
            doc.designMode = (/on|true/i).test(String(mode).toLowerCase()) ?'on':'off';
        }
    },

    // @private
    getDesignMode: function() {
        var doc = this.getDoc();
        return !doc ? '' : String(doc.designMode).toLowerCase();
    },

    disableItems: function(disabled) {
        var items = this.getToolbar().items.items,
            i,
            iLen  = items.length,
            item;

        for (i = 0; i < iLen; i++) {
            item = items[i];

            if (item.getItemId() !== 'sourceedit') {
                item.setDisabled(disabled);
            }
        }
    },

    /**
     * Toggles the editor between standard and source edit mode.
     * @param {Boolean} [sourceEditMode] True for source edit, false for standard
     */
    toggleSourceEdit: function(sourceEditMode) {
        var me = this,
            iframe = me.iframeEl,
            textarea = me.textareaEl,
            hiddenCls = Ext.baseCSSPrefix + 'hidden',
            btn = me.getToolbar().getComponent('sourceedit');

        if (!Ext.isBoolean(sourceEditMode)) {
            sourceEditMode = !me.sourceEditMode;
        }
        me.sourceEditMode = sourceEditMode;

        if (btn.pressed !== sourceEditMode) {
            btn.toggle(sourceEditMode);
        }
        if (sourceEditMode) {
            me.disableItems(true);
            me.syncValue();
            iframe.addCls(hiddenCls);
            textarea.removeCls(hiddenCls);
            textarea.dom.removeAttribute('tabIndex');
            textarea.focus();
            me.inputEl = textarea;
        } else {
            if (me.initialized) {
                me.disableItems(me.readOnly);
            }
            me.pushValue();
            iframe.removeCls(hiddenCls);
            textarea.addCls(hiddenCls);
            textarea.dom.setAttribute('tabIndex', -1);
            me.deferFocus();
            me.inputEl = iframe;
        }
        me.fireEvent('editmodechange', me, sourceEditMode);
        me.updateLayout();
    },

    // @private used internally
    createLink: function() {
        var url = prompt(this.createLinkText, this.defaultLinkValue);
        if (url && url !== 'http:/'+'/') {
            this.relayCmd('createlink', url);
        }
    },

    clearInvalid: Ext.emptyFn,

    setValue: function(value) {
        var me = this,
            textarea = me.textareaEl,
            inputCmp = me.inputCmp;
        
        if (value === null || value === undefined) {
            value = '';
        }
        if (textarea) {
            textarea.dom.value = value;
        }
        me.pushValue();
        
        if (!me.rendered && me.inputCmp) {
            me.inputCmp.data.value = value;
        }
        me.mixins.field.setValue.call(me, value);
        
        return me;
    },

    /**
     * If you need/want custom HTML cleanup, this is the method you should override.
     * @param {String} html The HTML to be cleaned
     * @return {String} The cleaned HTML
     * @protected
     */
    cleanHtml: function(html) {
        html = String(html);
        if (Ext.isWebKit) { // strip safari nonsense
            html = html.replace(/\sclass="(?:Apple-style-span|Apple-tab-span|khtml-block-placeholder)"/gi, '');
        }

        /*
         * Neat little hack. Strips out all the non-digit characters from the default
         * value and compares it to the character code of the first character in the string
         * because it can cause encoding issues when posted to the server. We need the
         * parseInt here because charCodeAt will return a number.
         */
        if (html.charCodeAt(0) === parseInt(this.defaultValue.replace(/\D/g, ''), 10)) {
            html = html.substring(1);
        }
        
        return html;
    },

    /**
     * Syncs the contents of the editor iframe with the textarea.
     * @protected
     */
    syncValue: function(){
        var me = this,
            body, changed, html, bodyStyle, match, textElDom;

        if (me.initialized) {
            body = me.getEditorBody();
            html = body.innerHTML;
            textElDom = me.textareaEl.dom;
            
            if (Ext.isWebKit) {
                bodyStyle = body.getAttribute('style'); // Safari puts text-align styles on the body element!
                match = bodyStyle.match(/text-align:(.*?);/i);
                if (match && match[1]) {
                    html = '<div style="' + match[0] + '">' + html + '</div>';
                }
            }
            
            html = me.cleanHtml(html);
            
            if (me.fireEvent('beforesync', me, html) !== false) {
                // Gecko inserts single <br> tag when input is empty
                // and user toggles source mode. See https://sencha.jira.com/browse/EXTJSIV-8542
                if (Ext.isGecko && textElDom.value === '' && html === '<br>') {
                    html = '';
                }
                
                if (textElDom.value !== html) {
                    textElDom.value = html;
                    changed = true;
                }

                me.fireEvent('sync', me, html);

                if (changed) {
                    // we have to guard this to avoid infinite recursion because getValue
                    // calls this method...
                    me.checkChange();
                }
            }
        }
    },

    getValue: function() {
        var me = this,
            value;
        if (!me.sourceEditMode) {
            me.syncValue();
        }
        value = me.rendered ? me.textareaEl.dom.value : me.value;
        me.value = value;
        return value;
    },

    /**
     * Pushes the value of the textarea into the iframe editor.
     * @protected
     */
    pushValue: function() {
        var me = this,
            v;
        if(me.initialized){
            v = me.textareaEl.dom.value || '';
            if (!me.activated && v.length < 1) {
                v = me.defaultValue;
            }
            if (me.fireEvent('beforepush', me, v) !== false) {
                me.getEditorBody().innerHTML = v;
                if (Ext.isGecko) {
                    // Gecko hack, see: https://bugzilla.mozilla.org/show_bug.cgi?id=232791#c8
                    me.setDesignMode(false);  //toggle off first
                    me.setDesignMode(true);
                }
                me.fireEvent('push', me, v);
            }
        }
    },

    // @private
    deferFocus: function(){
         this.focus(false, true);
    },

    getFocusEl: function() {
        var me = this,
            win = me.win;
        return win && !me.sourceEditMode ? win : me.textareaEl;
    },

    focus: function(selectText, delay) {
        var me = this,
            value, focusEl;

        if (delay) {
            if (!me.focusTask) {
                me.focusTask = new Ext.util.DelayedTask(me.focus);
            }
            me.focusTask.delay(Ext.isNumber(delay) ? delay : 10, null, me, [selectText, false]);
        }
        else {
            if (selectText) {
                if (me.textareaEl && me.textareaEl.dom) {
                    value = me.textareaEl.dom.value;
                }
                if (value && value.length) {  // Make sure there is content before calling SelectAll, otherwise the caret disappears.
                    me.execCmd('selectall', true);
                }
            }
            focusEl = me.getFocusEl();
            if (focusEl && focusEl.focus) {
                focusEl.focus();
            }
        }
        return me;
    },

    // @private
    initEditor: function(){
        //Destroying the component during/before initEditor can cause issues.
        try {
            var me = this,
                dbody = me.getEditorBody(),
                ss = me.textareaEl.getStyles('font-size', 'font-family', 'background-image', 'background-repeat', 'background-color', 'color'),
                doc,
                fn;

            ss['background-attachment'] = 'fixed'; // w3c
            dbody.bgProperties = 'fixed'; // ie

            Ext.DomHelper.applyStyles(dbody, ss);

            doc = me.getDoc();

            if (doc) {
                try {
                    Ext.EventManager.removeAll(doc);
                } catch(e) {}
            }

            /*
             * We need to use createDelegate here, because when using buffer, the delayed task is added
             * as a property to the function. When the listener is removed, the task is deleted from the function.
             * Since onEditorEvent is shared on the prototype, if we have multiple html editors, the first time one of the editors
             * is destroyed, it causes the fn to be deleted from the prototype, which causes errors. Essentially, we're just anonymizing the function.
             */
            fn = Ext.Function.bind(me.onEditorEvent, me);
            Ext.EventManager.on(doc, {
                mousedown: fn,
                dblclick: fn,
                click: fn,
                keyup: fn,
                buffer:100
            });
            
            // These events need to be relayed from the inner document (where they stop
            // bubbling) up to the outer document. This has to be done at the DOM level so
            // the event reaches listeners on elements like the document body. The effected
            // mechanisms that depend on this bubbling behavior are listed to the right
            // of the event.
            fn = me.onRelayedEvent;
            Ext.EventManager.on(doc, {
                mousedown: fn, // menu dismisal (MenuManager) and Window onMouseDown (toFront)
                mousemove: fn, // window resize drag detection
                mouseup: fn,   // window resize termination
                click: fn,     // not sure, but just to be safe
                dblclick: fn,  // not sure again
                scope: me
            });
            
            if (Ext.isGecko) {
                Ext.EventManager.on(doc, 'keypress', me.applyCommand, me);
            }
            
            if (me.fixKeys) {
                Ext.EventManager.on(doc, 'keydown', me.fixKeys, me);
            }
            if (me.fixKeysAfter) {
                Ext.EventManager.on(doc, 'keyup', me.fixKeysAfter, me);
            }

            if (Ext.isIE9 && Ext.isStrict) {
                Ext.EventManager.on(doc.documentElement, 'focus', me.focus, me);
            }

            // In old IEs, clicking on a toolbar button shifts focus from iframe
            // and it loses selection. To avoid this, we save current selection
            // and restore it.
            if (Ext.isIE8m || (Ext.isIE9 && !Ext.isStrict)) {
                Ext.EventManager.on(doc, 'focusout', function() {
                    me.savedSelection = doc.selection.type !== 'None' ? doc.selection.createRange() : null;
                }, me);
                
                Ext.EventManager.on(doc, 'focusin', function() {
                    if (me.savedSelection) {
                        me.savedSelection.select();
                    }
                }, me);
            }
            
            // We need to be sure we remove all our events from the iframe on unload or we're going to LEAK!
            Ext.EventManager.onWindowUnload(me.beforeDestroy, me);
            doc.editorInitialized = true;

            me.initialized = true;
            me.pushValue();
            me.setReadOnly(me.readOnly);
            me.fireEvent('initialize', me);
        } catch(ex) {
            // ignore (why?)
        }
    },
    
    // @private
    beforeDestroy: function(){
        var me = this,
            monitorTask = me.monitorTask,
            doc, prop;

        if (monitorTask) {
            Ext.TaskManager.stop(monitorTask);
        }
        if (me.rendered) {
            Ext.EventManager.removeUnloadListener(me.beforeDestroy, me);
            try {
                doc = me.getDoc();
                if (doc) {
                    // removeAll() doesn't currently know how to handle iframe document,
                    // so for now we have to wrap it in an Ext.Element using Ext.fly,
                    // or else IE6/7 will leak big time when the page is refreshed.
                    // TODO: this may not be needed once we find a more permanent fix.
                    // see EXTJSIV-5891.
                    Ext.EventManager.removeAll(Ext.fly(doc));
                    for (prop in doc) {
                        if (doc.hasOwnProperty && doc.hasOwnProperty(prop)) {
                            delete doc[prop];
                        }
                    }
                }
            } catch(e) {
                // ignore (why?)
            }
            me.iframeEl.remove();
            delete me.iframeEl;
            delete me.textareaEl;
            delete me.toolbar;
            delete me.inputCmp;
        }
        me.callParent();
    },

    // @private
    onRelayedEvent: function (event) {
        // relay event from the iframe's document to the document that owns the iframe...

        var iframeEl = this.iframeEl,

            // Get the left-based iframe position
            iframeXY = Ext.Element.getTrueXY(iframeEl),
            originalEventXY = event.getXY(),

            // Get the left-based XY position.
            // This is because the consumer of the injected event (Ext.EventManager) will
            // perform its own RTL normalization.
            eventXY = Ext.EventManager.getPageXY(event.browserEvent);

        // the event from the inner document has XY relative to that document's origin,
        // so adjust it to use the origin of the iframe in the outer document:
        event.xy = [iframeXY[0] + eventXY[0], iframeXY[1] + eventXY[1]];

        event.injectEvent(iframeEl); // blame the iframe for the event...

        event.xy = originalEventXY; // restore the original XY (just for safety)
    },

    // @private
    onFirstFocus: function(){
        var me = this,
            selection, range;

        me.activated = true;
        me.disableItems(me.readOnly);
        if (Ext.isGecko) { // prevent silly gecko errors
            me.win.focus();
            selection = me.win.getSelection();

            // If the editor contains a <br> tag, clicking on the editor after the text where
            // the <br> broke the line will produce nodeType === 1 (the body tag).
            // It's better to check the length of the selection.focusNode's content.
            //
            // If htmleditor.value = ' ' (note the space)
            // 1. nodeType === 1
            // 2. nodeName === 'BODY'
            // 3. selection.focusNode.textContent.length === 1
            //
            // If htmleditor.value = '' (no chars) nodeType === 3 && nodeName === '#text'
            // 1. nodeType === 3
            // 2. nodeName === '#text'
            // 3. selection.focusNode.textContent.length === 1 (yes, that's right, 1)
            //
            // The editor inserts Unicode code point 8203, a zero-width space when
            // htmleditor.value === '' (call selection.focusNode.textContent.charCodeAt(0))
            // http://www.fileformat.info/info/unicode/char/200b/index.htm
            // So, test with framework method to normalize.
            if (selection.focusNode && !me.getValue().length) {
                range = selection.getRangeAt(0);
                range.selectNodeContents(me.getEditorBody());
                range.collapse(true);
                me.deferFocus();
            }
            try {
                me.execCmd('useCSS', true);
                me.execCmd('styleWithCSS', false);
            } catch(e) {
                // ignore (why?)
            }
        }
        me.fireEvent('activate', me);
    },

    // @private
    adjustFont: function(btn) {
        var adjust = btn.getItemId() === 'increasefontsize' ? 1 : -1,
            size = this.getDoc().queryCommandValue('FontSize') || '2',
            isPxSize = Ext.isString(size) && size.indexOf('px') !== -1,
            isSafari;
        size = parseInt(size, 10);
        if (isPxSize) {
            // Safari 3 values
            // 1 = 10px, 2 = 13px, 3 = 16px, 4 = 18px, 5 = 24px, 6 = 32px
            if (size <= 10) {
                size = 1 + adjust;
            }
            else if (size <= 13) {
                size = 2 + adjust;
            }
            else if (size <= 16) {
                size = 3 + adjust;
            }
            else if (size <= 18) {
                size = 4 + adjust;
            }
            else if (size <= 24) {
                size = 5 + adjust;
            }
            else {
                size = 6 + adjust;
            }
            size = Ext.Number.constrain(size, 1, 6);
        } else {
            isSafari = Ext.isSafari;
            if (isSafari) { // safari
                adjust *= 2;
            }
            size = Math.max(1, size + adjust) + (isSafari ? 'px' : 0);
        }
        this.relayCmd('FontSize', size);
    },

    // @private
    onEditorEvent: function(e) {
        this.updateToolbar();
    },

    /**
     * Triggers a toolbar update by reading the markup state of the current selection in the editor.
     * @protected
     */
    updateToolbar: function() {
        var me = this,
            i, l, btns, doc, name, queriedName, fontSelect,
            toolbarSubmenus;

        if (me.readOnly) {
            return;
        }

        if (!me.activated) {
            me.onFirstFocus();
            return;
        }

        btns = me.getToolbar().items.map;
        doc = me.getDoc();

        if (me.enableFont && !Ext.isSafari2) {
            // When querying the fontName, Chrome may return an Array of font names
            // with those containing spaces being placed between single-quotes.
            queriedName = doc.queryCommandValue('fontName');
            name = (queriedName ? queriedName.split(",")[0].replace(me.reStripQuotes, '') : me.defaultFont).toLowerCase();
            fontSelect = me.fontSelect.dom;
            if (name !== fontSelect.value || name != queriedName) {
                fontSelect.value = name;
            }
        }

        function updateButtons() {
            var state;
            
            for (i = 0, l = arguments.length, name; i < l; i++) {
                name  = arguments[i];
                
                // Firefox 18+ sometimes throws NS_ERROR_INVALID_POINTER exception
                // See https://sencha.jira.com/browse/EXTJSIV-9766
                try {
                    state = doc.queryCommandState(name);
                }
                catch (e) {
                    state = false;
                }
                
                btns[name].toggle(state);
            }
        }
        if(me.enableFormat){
            updateButtons('bold', 'italic', 'underline');
        }
        if(me.enableAlignments){
            updateButtons('justifyleft', 'justifycenter', 'justifyright');
        }
        if(!Ext.isSafari2 && me.enableLists){
            updateButtons('insertorderedlist', 'insertunorderedlist');
        }

        // Ensure any of our toolbar's owned menus are hidden.
        // The overflow menu must control itself.
        toolbarSubmenus = me.toolbar.query('menu');
        for (i = 0; i < toolbarSubmenus.length; i++) {
            toolbarSubmenus[i].hide();
        }
        me.syncValue();
    },

    // @private
    relayBtnCmd: function(btn) {
        this.relayCmd(btn.getItemId());
    },

    /**
     * Executes a Midas editor command on the editor document and performs necessary focus and toolbar updates.
     * **This should only be called after the editor is initialized.**
     * @param {String} cmd The Midas command
     * @param {String/Boolean} [value=null] The value to pass to the command
     */
    relayCmd: function(cmd, value) {
        Ext.defer(function() {
            var me = this;
            
            if (!this.isDestroyed) {
                me.win.focus();
                me.execCmd(cmd, value);
                me.updateToolbar();
            }
        }, 10, this);
    },

    /**
     * Executes a Midas editor command directly on the editor document. For visual commands, you should use
     * {@link #relayCmd} instead. **This should only be called after the editor is initialized.**
     * @param {String} cmd The Midas command
     * @param {String/Boolean} [value=null] The value to pass to the command
     */
    execCmd: function(cmd, value){
        var me = this,
            doc = me.getDoc();
        doc.execCommand(cmd, false, (value == undefined ? null : value));
        me.syncValue();
    },

    // @private
    applyCommand: function(e){
        if (e.ctrlKey) {
            var me = this,
                c = e.getCharCode(), cmd;
            if (c > 0) {
                c = String.fromCharCode(c);
                switch (c) {
                    case 'b':
                        cmd = 'bold';
                    break;
                    case 'i':
                        cmd = 'italic';
                    break;
                    case 'u':
                        cmd = 'underline';
                    break;
                }
                if (cmd) {
                    me.win.focus();
                    me.execCmd(cmd);
                    me.deferFocus();
                    e.preventDefault();
                }
            }
        }
    },

    /**
     * Inserts the passed text at the current cursor position.
     * __Note:__ the editor must be initialized and activated to insert text.
     * @param {String} text
     */
    insertAtCursor: function(text){
        var me = this,
            range;

        if (me.activated) {
            me.win.focus();
            if (Ext.isIE) {
                range = me.getDoc().selection.createRange();
                if (range) {
                    range.pasteHTML(text);
                    me.syncValue();
                    me.deferFocus();
                }
            }else{
                me.execCmd('InsertHTML', text);
                me.deferFocus();
            }
        }
    },

    // @private
    fixKeys: (function() { // load time branching for fastest keydown performance
        if (Ext.isIE) {
            return function(e){
                var me = this,
                    k = e.getKey(),
                    doc = me.getDoc(),
                    readOnly = me.readOnly,
                    range, target;

                if (k === e.TAB) {
                    e.stopEvent();
                    if (!readOnly) {
                        range = doc.selection.createRange();
                        if (range){
                            if (range.collapse) {
                                range.collapse(true);
                                range.pasteHTML('&#160;&#160;&#160;&#160;');
                            }
                            
                            me.deferFocus();
                        }
                    }
                }
                else if (k === e.ENTER) {
                    if (!readOnly) {
                        range = doc.selection.createRange();
                        if (range) {
                            target = range.parentElement();
                            if(!target || target.tagName.toLowerCase() !== 'li'){
                                e.stopEvent();
                                range.pasteHTML('<br />');
                                range.collapse(false);
                                range.select();
                            }
                        }
                    }
                }
            };
        }

        if (Ext.isOpera) {
            return function(e){
                var me = this,
                    k = e.getKey(),
                    readOnly = me.readOnly;
                if (k === e.TAB) {
                    e.stopEvent();
                    if (!readOnly) {
                        me.win.focus();
                        me.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
                        me.deferFocus();
                    }
                }
            };
        }

        return null; // not needed, so null
    }()),
    
    // @private
    fixKeysAfter: (function() {
        if (Ext.isIE) {
            return function(e) {
                var me = this,
                    k = e.getKey(),
                    doc = me.getDoc(),
                    readOnly = me.readOnly,
                    innerHTML;
                
                if (!readOnly && (k === e.BACKSPACE || k === e.DELETE)) {
                    innerHTML = doc.body.innerHTML;
                    
                    // If HtmlEditor had some input and user cleared it, IE inserts <p>&nbsp;</p>
                    // which makes an impression that there is still some text, and creeps
                    // into source mode when toggled. We don't want this.
                    //
                    // See https://sencha.jira.com/browse/EXTJSIV-8542
                    // 
                    // N.B. There is **small** chance that user could go to source mode,
                    // type '<p>&nbsp;</p>', switch back to visual mode, type something else
                    // and then clear it -- the code below would clear the <p> tag as well,
                    // which could be considered a bug. However I see no way to distinguish
                    // between offending markup being entered manually and generated by IE,
                    // so this can be considered a nasty corner case.
                    //
                    if (innerHTML === '<p>&nbsp;</p>' || innerHTML === '<P>&nbsp;</P>') {
                        doc.body.innerHTML = '';
                    }
                }
            }
        }
        
        return null;
    }()),

    /**
     * Returns the editor's toolbar. **This is only available after the editor has been rendered.**
     * @return {Ext.toolbar.Toolbar}
     */
    getToolbar: function(){
        return this.toolbar;
    },

    //<locale>
    /**
     * @property {Object} buttonTips
     * Object collection of toolbar tooltips for the buttons in the editor. The key is the command id associated with
     * that button and the value is a valid QuickTips object. For example:
     *
     *     {
     *         bold: {
     *             title: 'Bold (Ctrl+B)',
     *             text: 'Make the selected text bold.',
     *             cls: 'x-html-editor-tip'
     *         },
     *         italic: {
     *             title: 'Italic (Ctrl+I)',
     *             text: 'Make the selected text italic.',
     *             cls: 'x-html-editor-tip'
     *         }
     *         // ...
     *     }
     */
    buttonTips: {
        bold: {
            title: 'Bold (Ctrl+B)',
            text: 'Make the selected text bold.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        italic: {
            title: 'Italic (Ctrl+I)',
            text: 'Make the selected text italic.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        underline: {
            title: 'Underline (Ctrl+U)',
            text: 'Underline the selected text.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        increasefontsize: {
            title: 'Grow Text',
            text: 'Increase the font size.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        decreasefontsize: {
            title: 'Shrink Text',
            text: 'Decrease the font size.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        backcolor: {
            title: 'Text Highlight Color',
            text: 'Change the background color of the selected text.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        forecolor: {
            title: 'Font Color',
            text: 'Change the color of the selected text.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        justifyleft: {
            title: 'Align Text Left',
            text: 'Align text to the left.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        justifycenter: {
            title: 'Center Text',
            text: 'Center text in the editor.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        justifyright: {
            title: 'Align Text Right',
            text: 'Align text to the right.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        insertunorderedlist: {
            title: 'Bullet List',
            text: 'Start a bulleted list.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        insertorderedlist: {
            title: 'Numbered List',
            text: 'Start a numbered list.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        createlink: {
            title: 'Hyperlink',
            text: 'Make the selected text a hyperlink.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        sourceedit: {
            title: 'Source Edit',
            text: 'Switch to source editing mode.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        }
    }
    //</locale>

    // hide stuff that is not compatible
    /**
     * @event blur
     * @private
     */
    /**
     * @event focus
     * @private
     */
    /**
     * @event specialkey
     * @private
     */
    /**
     * @cfg {String} fieldCls
     * @private
     */
    /**
     * @cfg {String} focusCls
     * @private
     */
    /**
     * @cfg {String} autoCreate
     * @private
     */
    /**
     * @cfg {String} inputType
     * @private
     */
    /**
     * @cfg {String} invalidCls
     * @private
     */
    /**
     * @cfg {String} invalidText
     * @private
     */
    /**
     * @cfg {String} msgFx
     * @private
     */
    /**
     * @cfg {Boolean} allowDomMove
     * @private
     */
    /**
     * @cfg {String} applyTo
     * @private
     */
    /**
     * @cfg {String} readOnly
     * @private
     */
    /**
     * @cfg {String} tabIndex
     * @private
     */
    /**
     * @method validate
     * @private
     */
});

/**
 * A time picker which provides a list of times from which to choose. This is used by the Ext.form.field.Time
 * class to allow browsing and selection of valid times, but could also be used with other components.
 *
 * By default, all times starting at midnight and incrementing every 15 minutes will be presented. This list of
 * available times can be controlled using the {@link #minValue}, {@link #maxValue}, and {@link #increment}
 * configuration properties. The format of the times presented in the list can be customized with the {@link #format}
 * config.
 *
 * To handle when the user selects a time from the list, you can subscribe to the {@link #selectionchange} event.
 *
 *     @example
 *     Ext.create('Ext.picker.Time', {
 *        width: 60,
 *        minValue: Ext.Date.parse('04:30:00 AM', 'h:i:s A'),
 *        maxValue: Ext.Date.parse('08:00:00 AM', 'h:i:s A'),
 *        renderTo: Ext.getBody()
 *     });
 */
Ext.define('Ext.picker.Time', {
    extend:  Ext.view.BoundList ,
    alias: 'widget.timepicker',
                                             

    /**
     * @cfg {Date} minValue
     * The minimum time to be shown in the list of times. This must be a Date object (only the time fields will be
     * used); no parsing of String values will be done.
     */

    /**
     * @cfg {Date} maxValue
     * The maximum time to be shown in the list of times. This must be a Date object (only the time fields will be
     * used); no parsing of String values will be done.
     */

    /**
     * @cfg {Number} increment
     * The number of minutes between each time value in the list.
     */
    increment: 15,

    //<locale>
    /**
     * @cfg {String} [format=undefined]
     * The default time format string which can be overriden for localization support. The format must be valid
     * according to {@link Ext.Date#parse}.
     *
     * Defaults to `'g:i A'`, e.g., `'3:15 PM'`. For 24-hour time format try `'H:i'` instead.
     */
    format : "g:i A",
    //</locale>

    /**
     * @private
     * The field in the implicitly-generated Model objects that gets displayed in the list. This is
     * an internal field name only and is not useful to change via config.
     */
    displayField: 'disp',

    /**
     * @private
     * Year, month, and day that all times will be normalized into internally.
     */
    initDate: [2008,0,1],

    componentCls: Ext.baseCSSPrefix + 'timepicker',

    /**
     * @cfg
     * @private
     */
    loadMask: false,

    initComponent: function() {
        var me = this,
            dateUtil = Ext.Date,
            clearTime = dateUtil.clearTime,
            initDate = me.initDate;

        // Set up absolute min and max for the entire day
        me.absMin = clearTime(new Date(initDate[0], initDate[1], initDate[2]));
        me.absMax = dateUtil.add(clearTime(new Date(initDate[0], initDate[1], initDate[2])), 'mi', (24 * 60) - 1);

        me.store = me.createStore();

        // Add our min/max range filter, but do not apply it.
        // The owning TimeField will filter it.
        me.store.addFilter(me.rangeFilter = new Ext.util.Filter({
            id: 'time-picker-filter'
        }), false);

        // Updates the range filter's filterFn according to our configured min and max
        me.updateList();

        me.callParent();
    },

    /**
     * Set the {@link #minValue} and update the list of available times. This must be a Date object (only the time
     * fields will be used); no parsing of String values will be done.
     * @param {Date} value
     */
    setMinValue: function(value) {
        this.minValue = value;
        this.updateList();
    },

    /**
     * Set the {@link #maxValue} and update the list of available times. This must be a Date object (only the time
     * fields will be used); no parsing of String values will be done.
     * @param {Date} value
     */
    setMaxValue: function(value) {
        this.maxValue = value;
        this.updateList();
    },

    /**
     * @private
     * Sets the year/month/day of the given Date object to the {@link #initDate}, so that only
     * the time fields are significant. This makes values suitable for time comparison.
     * @param {Date} date
     */
    normalizeDate: function(date) {
        var initDate = this.initDate;
        date.setFullYear(initDate[0], initDate[1], initDate[2]);
        return date;
    },

    /**
     * Update the list of available times in the list to be constrained within the {@link #minValue}
     * and {@link #maxValue}.
     */
    updateList: function() {
        var me = this,
            min = me.normalizeDate(me.minValue || me.absMin),
            max = me.normalizeDate(me.maxValue || me.absMax);

        me.rangeFilter.setFilterFn(function(record) {
            var date = record.get('date');
            return date >= min && date <= max;
        });
        me.store.filter();
    },

    /**
     * @private
     * Creates the internal {@link Ext.data.Store} that contains the available times. The store
     * is loaded with all possible times, and it is later filtered to hide those times outside
     * the minValue/maxValue.
     */
    createStore: function() {
        var me = this,
            utilDate = Ext.Date,
            times = [],
            min = me.absMin,
            max = me.absMax;

        while(min <= max){
            times.push({
                disp: utilDate.dateFormat(min, me.format),
                date: min
            });
            min = utilDate.add(min, 'mi', me.increment);
        }

        return new Ext.data.Store({
            fields: ['disp', 'date'],
            data: times
        });
    },

    focusNode: function (rec) {
        // We don't want the view being focused when interacting with the inputEl (see Ext.form.field.ComboBox:onKeyUp)
        // so this is here to prevent focus of the boundlist view. See EXTJSIV-7319.
        return false;
    }

});

/**
 * Provides a time input field with a time dropdown and automatic time validation.
 *
 * This field recognizes and uses JavaScript Date objects as its main {@link #value} type (only the time portion of the
 * date is used; the month/day/year are ignored). In addition, it recognizes string values which are parsed according to
 * the {@link #format} and/or {@link #altFormats} configs. These may be reconfigured to use time formats appropriate for
 * the user's locale.
 *
 * The field may be limited to a certain range of times by using the {@link #minValue} and {@link #maxValue} configs,
 * and the interval between time options in the dropdown can be changed with the {@link #increment} config.
 *
 * Example usage:
 *
 *     @example
 *     Ext.create('Ext.form.Panel', {
 *         title: 'Time Card',
 *         width: 300,
 *         bodyPadding: 10,
 *         renderTo: Ext.getBody(),
 *         items: [{
 *             xtype: 'timefield',
 *             name: 'in',
 *             fieldLabel: 'Time In',
 *             minValue: '6:00 AM',
 *             maxValue: '8:00 PM',
 *             increment: 30,
 *             anchor: '100%'
 *         }, {
 *             xtype: 'timefield',
 *             name: 'out',
 *             fieldLabel: 'Time Out',
 *             minValue: '6:00 AM',
 *             maxValue: '8:00 PM',
 *             increment: 30,
 *             anchor: '100%'
 *        }]
 *     });
 */
Ext.define('Ext.form.field.Time', {
    extend: Ext.form.field.ComboBox ,
    alias: 'widget.timefield',
                                                                                                 
    alternateClassName: ['Ext.form.TimeField', 'Ext.form.Time'],

    /**
     * @cfg {String} [triggerCls='x-form-time-trigger']
     * An additional CSS class used to style the trigger button. The trigger will always get the {@link #triggerBaseCls}
     * by default and triggerCls will be **appended** if specified.
     */
    triggerCls: Ext.baseCSSPrefix + 'form-time-trigger',

    /**
     * @cfg {Date/String} minValue
     * The minimum allowed time. Can be either a Javascript date object with a valid time value or a string time in a
     * valid format -- see {@link #format} and {@link #altFormats}.
     */

    /**
     * @cfg {Date/String} maxValue
     * The maximum allowed time. Can be either a Javascript date object with a valid time value or a string time in a
     * valid format -- see {@link #format} and {@link #altFormats}.
     */

    //<locale>
    /**
     * @cfg {String} minText
     * The error text to display when the entered time is before {@link #minValue}.
     */
    minText : "The time in this field must be equal to or after {0}",
    //</locale>

    //<locale>
    /**
     * @cfg {String} maxText
     * The error text to display when the entered time is after {@link #maxValue}.
     */
    maxText : "The time in this field must be equal to or before {0}",
    //</locale>

    //<locale>
    /**
     * @cfg {String} invalidText
     * The error text to display when the time in the field is invalid.
     */
    invalidText : "{0} is not a valid time",
    //</locale>

    //<locale>
    /**
     * @cfg {String} [format=undefined]
     * The default time format string which can be overriden for localization support. The format must be valid
     * according to {@link Ext.Date#parse}.
     *
     * Defaults to `'g:i A'`, e.g., `'3:15 PM'`. For 24-hour time format try `'H:i'` instead.
     */
    format : "g:i A",
    //</locale>

    //<locale>
    /**
     * @cfg {String} [submitFormat=undefined]
     * The date format string which will be submitted to the server. The format must be valid according to
     * {@link Ext.Date#parse}.
     *
     * Defaults to {@link #format}.
     */
    //</locale>

    //<locale>
    /**
     * @cfg {String} altFormats
     * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
     * format.
     */
    altFormats : "g:ia|g:iA|g:i a|g:i A|h:i|g:i|H:i|ga|ha|gA|h a|g a|g A|gi|hi|gia|hia|g|H|gi a|hi a|giA|hiA|gi A|hi A",
    //</locale>

    /**
     * @cfg {Number} increment
     * The number of minutes between each time value in the list.
     */
    increment: 15,

    /**
     * @cfg {Number} pickerMaxHeight
     * The maximum height of the {@link Ext.picker.Time} dropdown.
     */
    pickerMaxHeight: 300,

    /**
     * @cfg {Boolean} selectOnTab
     * Whether the Tab key should select the currently highlighted item.
     */
    selectOnTab: true,
    
    /**
     * @cfg {Boolean} [snapToIncrement=false]
     * Specify as `true` to enforce that only values on the {@link #increment} boundary are accepted.
     */
    snapToIncrement: false,

    /**
     * @private
     * This is the date to use when generating time values in the absence of either minValue
     * or maxValue.  Using the current date causes DST issues on DST boundary dates, so this is an
     * arbitrary "safe" date that can be any date aside from DST boundary dates.
     */
    initDate: '1/1/2008',
    initDateParts: [2008, 0, 1],
    initDateFormat: 'j/n/Y',
    
    ignoreSelection: 0,

    queryMode: 'local',

    displayField: 'disp',

    valueField: 'date',

    initComponent: function() {
        var me = this,
            min = me.minValue,
            max = me.maxValue;
        if (min) {
            me.setMinValue(min);
        }
        if (max) {
            me.setMaxValue(max);
        }
        me.displayTpl = new Ext.XTemplate(
            '<tpl for=".">' +
                '{[typeof values === "string" ? values : this.formatDate(values["' + me.displayField + '"])]}' +
                '<tpl if="xindex < xcount">' + me.delimiter + '</tpl>' +
            '</tpl>', {
            formatDate: Ext.Function.bind(me.formatDate, me)
        });
        this.callParent();
    },

    /**
     * @private
     */
    transformOriginalValue: function (value) {
        if (Ext.isDefined(value)) {
            return this.rawToValue(value) || value || null;
        }
        return value;
    },

    /**
     * @private
     */
    isEqual: function (v1, v2) {
        var fromArray = Ext.Array.from,
            isEqual = Ext.Date.isEqual,
            i, len;

        v1 = fromArray(v1);
        v2 = fromArray(v2);
        len = v1.length;

        if (len !== v2.length) {
            return false;
        }

        for (i = 0; i < len; i++) {
            if (!isEqual(v2[i], v1[i])) {
                return false;
            }
        }

        return true;
    },

    /**
     * Replaces any existing {@link #minValue} with the new time and refreshes the picker's range.
     * @param {Date/String} value The minimum time that can be selected
     */
    setMinValue: function(value) {
        var me = this,
            picker = me.picker;
        me.setLimit(value, true);
        if (picker) {
            picker.setMinValue(me.minValue);
        }
    },

    /**
     * Replaces any existing {@link #maxValue} with the new time and refreshes the picker's range.
     * @param {Date/String} value The maximum time that can be selected
     */
    setMaxValue: function(value) {
        var me = this,
            picker = me.picker;
        me.setLimit(value, false);
        if (picker) {
            picker.setMaxValue(me.maxValue);
        }
    },

    /**
     * @private
     * Updates either the min or max value. Converts the user's value into a Date object whose
     * year/month/day is set to the {@link #initDate} so that only the time fields are significant.
     */
    setLimit: function(value, isMin) {
        var me = this,
            d, val;
        if (Ext.isString(value)) {
            d = me.parseDate(value);
        }
        else if (Ext.isDate(value)) {
            d = value;
        }
        if (d) {
            val = me.getInitDate();
            val.setHours(d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds());
        }
        // Invalid min/maxValue config should result in a null so that defaulting takes over
        else {
            val = null;
        }
        me[isMin ? 'minValue' : 'maxValue'] = val;
    },
    
    getInitDate: function(hours, minutes) {
        var parts = this.initDateParts;
        return new Date(parts[0], parts[1], parts[2], hours || 0, minutes || 0, 0, 0);    
    },

    valueToRaw: function(value) {
        return this.formatDate(this.parseDate(value));
    },

    /**
     * Runs all of Time's validations and returns an array of any errors. Note that this first runs Text's validations,
     * so the returned array is an amalgamation of all field errors. The additional validation checks are testing that
     * the time format is valid, that the chosen time is within the {@link #minValue} and {@link #maxValue} constraints
     * set.
     * @param {Object} [value] The value to get errors for (defaults to the current field value)
     * @return {String[]} All validation errors for this field
     */
    getErrors: function(value) {
        var me = this,
            format = Ext.String.format,
            errors = me.callParent(arguments),
            minValue = me.minValue,
            maxValue = me.maxValue,
            data = me.displayTplData,
            raw = me.getRawValue(),
            i, len, date, item;

        if (data && data.length > 0) {
            for (i = 0, len = data.length; i < len; i++ ){
                item = data[i];
                item = item.date || item.disp;
                date = me.parseDate(item);
                if (!date) {
                    errors.push(format(me.invalidText, item, Ext.Date.unescapeFormat(me.format)));
                    continue;
                }

                if (minValue && date < minValue) {
                    errors.push(format(me.minText, me.formatDate(minValue)));
                }

                if (maxValue && date > maxValue) {
                    errors.push(format(me.maxText, me.formatDate(maxValue)));
                }
            }
        } else if (raw.length && !me.parseDate(raw)) {
            // If we don't have any data & a rawValue, it means an invalid time was entered.
            errors.push(format(me.invalidText, raw, Ext.Date.unescapeFormat(me.format)));
        }

        return errors;
    },

    formatDate: function(items) {
        var formatted = [],
            i, len;

        items = Ext.Array.from(items);

        for (i = 0, len = items.length; i < len; i++) {
            formatted.push(Ext.form.field.Date.prototype.formatDate.call(this, items[i]));
        }

        return formatted.join(this.delimiter);
    },

    /**
     * @private
     * Parses an input value into a valid Date object.
     * @param {String/Date} value
     */
    parseDate: function(value) {
        var me = this,
            val = value,
            altFormats = me.altFormats,
            altFormatsArray = me.altFormatsArray,
            i = 0,
            len;

        if (value && !Ext.isDate(value)) {
            val = me.safeParse(value, me.format);

            if (!val && altFormats) {
                altFormatsArray = altFormatsArray || altFormats.split('|');
                len = altFormatsArray.length;
                for (; i < len && !val; ++i) {
                    val = me.safeParse(value, altFormatsArray[i]);
                }
            }
        }

        // If configured to snap, snap resulting parsed Date to the closest increment.
        if (val && me.snapToIncrement) {
            val = new Date(Ext.Number.snap(val.getTime(), me.increment * 60 * 1000));
        }
        return val;
    },

    safeParse: function(value, format){
        var me = this,
            utilDate = Ext.Date,
            parsedDate,
            result = null;

        if (utilDate.formatContainsDateInfo(format)) {
            // assume we've been given a full date
            result = utilDate.parse(value, format);
        } else {
            // Use our initial safe date
            parsedDate = utilDate.parse(me.initDate + ' ' + value, me.initDateFormat + ' ' + format);
            if (parsedDate) {
                result = parsedDate;
            }
        }
        return result;
    },

    // @private
    getSubmitValue: function() {
        var me = this,
            format = me.submitFormat || me.format,
            value = me.getValue();

        return value ? Ext.Date.format(value, format) : null;
    },

    /**
     * @private
     * Creates the {@link Ext.picker.Time}
     */
    createPicker: function() {
        var me = this,
            picker;

        me.listConfig = Ext.apply({
            xtype: 'timepicker',
            selModel: {
                mode: me.multiSelect ? 'SIMPLE' : 'SINGLE'
            },
            cls: undefined,
            minValue: me.minValue,
            maxValue: me.maxValue,
            increment: me.increment,
            format: me.format,
            maxHeight: me.pickerMaxHeight
        }, me.listConfig);
        picker = me.callParent();
        me.bindStore(picker.store);
        return picker;
    },
    
    onItemClick: function(picker, record){
        // The selection change events won't fire when clicking on the selected element. Detect it here.
        var me = this,
            selected = picker.getSelectionModel().getSelection();

        if (!me.multiSelect && selected.length) {
            if (selected.length > 0) {
                selected = selected[0];
                if (selected && Ext.Date.isEqual(record.get('date'), selected.get('date'))) {
                    me.collapse();
                }
            }
        }
    },

    /**
     * @private 
     * Synchronizes the selection in the picker to match the current value
     */
    syncSelection: function() {
        var me = this,
            picker = me.picker,
            isEqual = Ext.Date.isEqual,
            toSelect = [],
            selModel,
            value, values, i, len, item,
            data, d, dLen, rec;
            
        if (picker) {
            picker.clearHighlight();
            value = me.getValue();
            selModel = picker.getSelectionModel();
            // Update the selection to match
            me.ignoreSelection++;
            if (value === null) {
                selModel.deselectAll();
            } else {
                values = Ext.Array.from(value);
                data = picker.store.data.items;
                dLen = data.length;

                for (i = 0, len = values.length; i < len; i++) {
                    item = values[i];
                    if (Ext.isDate(item)) {
                        // find value, select it
                        for (d = 0; d < dLen; d++) {
                            rec = data[d];

                            if (isEqual(rec.get('date'), item)) {
                               toSelect.push(rec);
                               if (!me.multiSelect) {
                                   break;
                               }
                           }
                        }
                        selModel.select(toSelect);
                    }
                }
            }
            me.ignoreSelection--;
        }
    },

    postBlur: function() {
        var me = this,
            val = me.getValue();

        me.callParent(arguments);

        // Only set the raw value if the current value is valid and is not falsy
        if (me.validateValue(val)) {
            me.setValue(val);
        }
    },

    /**
     * Finds the record by searching values in the {@link #valueField}.
     * @param {Object/String} value The value to match the field against.
     * @return {Ext.data.Model} The matched record or false.
     */
    findRecordByValue: function (value) {
        if (typeof value === 'string') {
            value = this.parseDate(value);
        }
        return this.callParent([value]);
    },

    rawToValue: function (item) {
        var me = this,
            items, values, i, len;

        if (me.multiSelect) {
            values = [];
            items = Ext.Array.from(item);

            for (i = 0, len = items.length; i < len; i++) {
                values.push(me.parseDate(items[i]));
            }

            return values;
        }

        return me.parseDate(item);
    },

    setValue: function(v) {
        // Store MUST be created for parent setValue to function
        this.getPicker();
        if (Ext.isDate(v)) {
            v = this.getInitDate(v.getHours(), v.getMinutes());
        }

        return this.callParent([v]);
    },

    getValue: function () {
        return this.rawToValue(this.callParent(arguments));
    }
});

/**
 * Internal utility class that provides a unique cell context.
 * @private
 */
Ext.define('Ext.grid.CellContext', {

    /**
     * @property {Boolean} isCellContext
     * @readonly
     * `true` in this class to identify an object as an instantiated CellContext, or subclass thereof.
     */
    isCellContext: true,
    
    constructor: function(view) {
        this.view = view;
    },
    
    isEqual: function(other) {
        if (other) {
            return this.record === other.record && this.columnHeader === other.columnHeader;
        }
        return false;
    },
    
    // Selection row/record & column/columnHeader
    setPosition: function(row, col) {
        var me = this;

        // We were passed {row: 1, column: 2, view: myView}
        if (arguments.length === 1) {
            if (row.view) {
                me.view = row.view;
            }
            col = row.column;
            row = row.row;
        }

        me.setRow(row);
        me.setColumn(col);
        return me;
    },

    setRow: function(row) {
        var me = this;
        if (row !== undefined) {
            // Row index passed
            if (typeof row === 'number') {
                me.row = Math.max(Math.min(row, me.view.dataSource.getCount() - 1), 0);
                me.record = me.view.dataSource.getAt(row);
            }
            // row is a Record
            else if (row.isModel) {
                me.record = row;
                me.row = me.view.indexOf(row);
            }
            // row is a grid row
            else if (row.tagName) {
                me.record = me.view.getRecord(row);
                me.row = me.view.indexOf(me.record);
            }
        }
    },
    
    setColumn: function(col) {
        var me = this,
            mgr = me.view.ownerCt.getColumnManager();
            
        if (col !== undefined) {
            if (typeof col === 'number') {
                me.column = col;
                me.columnHeader = mgr.getHeaderAtIndex(col);
            } else if (col.isHeader) {
                me.columnHeader = col;
                me.column = mgr.getHeaderIndex(col);
            }
        }
    }
});

/**
 * Internal utility class that provides default configuration for cell editing.
 * @private
 */
Ext.define('Ext.grid.CellEditor', {
    extend:  Ext.Editor ,
    constructor: function(config) {
        config = Ext.apply({}, config);
        
        if (config.field) {
            config.field.monitorTab = false;
        }
        this.callParent([config]);
    },
    
    /**
     * @private
     * Hides the grid cell inner element when a cell editor is shown.
     */
    onShow: function() {
        var me = this,
            innerCell = me.boundEl.first();

        if (innerCell) {
            if (me.isForTree) {
                innerCell = innerCell.child(me.treeNodeSelector);
            }
            innerCell.hide();
        }

        me.callParent(arguments);
    },

    /**
     * @private
     * Shows the grid cell inner element when a cell editor is hidden
     */
    onHide: function() {
        var me = this,
            innerCell = me.boundEl.first();

        if (innerCell) {
            if (me.isForTree) {
                innerCell = innerCell.child(me.treeNodeSelector);
            }
            innerCell.show();
        }
        
        me.callParent(arguments);
    },

    /**
     * @private
     * Fix checkbox blur when it is clicked.
     */
    afterRender: function() {
        var me = this,
            field = me.field;

        me.callParent(arguments);
        if (field.isCheckbox) {
            field.mon(field.inputEl, {
                mousedown: me.onCheckBoxMouseDown,
                click: me.onCheckBoxClick,
                scope: me
            });
        }
    },
    
    /**
     * @private
     * Because when checkbox is clicked it loses focus  completeEdit is bypassed.
     */
    onCheckBoxMouseDown: function() {
        this.completeEdit = Ext.emptyFn;
    },
    
    /**
     * @private
     * Restore checkbox focus and completeEdit method.
     */
    onCheckBoxClick: function() {
        delete this.completeEdit;
        this.field.focus(false, 10);
    },
    
    /**
     * @private
     * Realigns the Editor to the grid cell, or to the text node in the grid inner cell
     * if the inner cell contains multiple child nodes.
     */
    realign: function(autoSize) {
        var me = this,
            boundEl = me.boundEl,
            innerCell = boundEl.first(),
            innerCellTextNode = innerCell.dom.firstChild,
            width = boundEl.getWidth(),
            offsets = Ext.Array.clone(me.offsets),
            grid = me.grid,
            xOffset,
            v = '',

            // innerCell is empty if there are no children, or there is one text node, and it contains whitespace
            isEmpty = !innerCellTextNode || (innerCellTextNode.nodeType === 3 && !(Ext.String.trim(v = innerCellTextNode.data).length));

        if (me.isForTree) {
            // When editing a tree, adjust the width and offsets of the editor to line
            // up with the tree cell's text element
            xOffset = me.getTreeNodeOffset(innerCell);
            width -= Math.abs(xOffset);
            offsets[0] += xOffset;
        }

        if (grid.columnLines) {
            // Subtract the column border width so that the editor displays inside the
            // borders. The column border could be either on the left or the right depending
            // on whether the grid is RTL - using the sum of both borders works in both modes. 
            width -= boundEl.getBorderWidth('rl');
        }

        if (autoSize === true) {
            me.field.setWidth(width);
        }

        // https://sencha.jira.com/browse/EXTJSIV-10871 Ensure the data bearing element has a height from text.
        if (isEmpty) {
            innerCell.dom.innerHTML = 'X';
        }
        me.alignTo(innerCell, me.alignment, offsets);
        if (isEmpty) {
            innerCell.dom.firstChild.data = v;
        }
    },

    // private
    getTreeNodeOffset: function(innerCell) {
        return innerCell.child(this.treeNodeSelector).getOffsetsTo(innerCell)[0];
    },

    onEditorTab: function(e){
        var field = this.field;
        if (field.onEditorTab) {
            field.onEditorTab(e);
        }
    },

    onFieldBlur : function() {
        this.callParent(arguments);
        // Reset the flag that may have been set by CellEditing#startEdit to prevent
        // Ext.Editor#onFieldBlur from canceling editing.
        this.selectSameEditor = false;
    },

    alignment: "l-l",
    hideEl : false,
    cls: Ext.baseCSSPrefix + 'small-editor ' +
        Ext.baseCSSPrefix + 'grid-editor ' + 
        Ext.baseCSSPrefix + 'grid-cell-editor',
    treeNodeSelector: '.' + Ext.baseCSSPrefix + 'tree-node-text',
    shim: false,
    shadow: false
});

/**
 * Component layout for grid column headers which have a title element at the top followed by content.
 * @private
 */
Ext.define('Ext.grid.ColumnComponentLayout', {
    extend:  Ext.layout.component.Auto ,
    alias: 'layout.columncomponent',

    type: 'columncomponent',

    setWidthInDom: true,

    beginLayout: function(ownerContext) {
        this.callParent(arguments);
        ownerContext.titleContext = ownerContext.getEl('titleEl');
        ownerContext.triggerContext = ownerContext.getEl('triggerEl');
    },

    beginLayoutCycle: function(ownerContext) {
        var owner = this.owner;

        this.callParent(arguments);

        // If shrinkwrapping, allow content width to stretch the element
        if (ownerContext.widthModel.shrinkWrap) {
            owner.el.setWidth('');
        }

        owner.titleEl.setStyle({
            paddingTop: '',  // reset back to default padding of the style
            paddingBottom: ''
        });
    },

    // If not shrink wrapping, push height info down into child items
    publishInnerHeight: function(ownerContext, outerHeight) {
        // TreePanels (and grids with hideHeaders: true) set their column container height to zero ti hide them.
        // This is because they need to lay out in order to calculate widths for the columns (eg flexes).
        // If there is no height to lay out, bail out early.
        if (!outerHeight) {
            return;
        }

        var me = this,
            owner = me.owner,
            innerHeight = outerHeight - ownerContext.getBorderInfo().height,
            availableHeight = innerHeight,
            textHeight,
            titleHeight,
            pt, pb;

        // We do not have enough information to get the height of the titleEl
        if (!owner.noWrap && !ownerContext.hasDomProp('width')) {
            me.done = false;
            return;
        }

        // If we are not a container, but just have HTML content...
        if (ownerContext.hasRawContent) {
            titleHeight = availableHeight;

            // Vertically center the header text and ensure titleContext occupies availableHeight
            textHeight = owner.textEl.getHeight();
            if (textHeight) {
                availableHeight -= textHeight;
                if (availableHeight > 0) {
                    pt = Math.floor(availableHeight / 2);
                    pb = availableHeight - pt;
                    ownerContext.titleContext.setProp('padding-top', pt);
                    ownerContext.titleContext.setProp('padding-bottom', pb);
                }
            }
        }

        // There are child items
        else {
            titleHeight = owner.titleEl.getHeight();
            ownerContext.setProp('innerHeight', innerHeight - titleHeight, false);
        }
        // Only IE6 and IEQuirks needs this.
        // This is why we maintain titleHeight when setting it.
        if ((Ext.isIE6 || Ext.isIEQuirks) && ownerContext.triggerContext) {
            ownerContext.triggerContext.setHeight(titleHeight);
        }

    },

    // We do not need the Direct2D sub pixel measurement here. Just the offsetHeight will do.
    // TODO: When https://sencha.jira.com/browse/EXTJSIV-7734 is fixed to not do subpixel adjustment on height,
    // remove this override.
    measureContentHeight: function(ownerContext) {
        return ownerContext.el.dom.offsetHeight;
    },

    publishOwnerHeight: function(ownerContext, contentHeight) {
        this.callParent(arguments);
        // Only IE6 and IEQuirks needs this.
        // This is why we maintain titleHeight when setting it.
        if ((Ext.isIE6 || Ext.isIEQuirks) && ownerContext.triggerContext) {
            ownerContext.triggerContext.setHeight(contentHeight);
        }
    },

    // If not shrink wrapping, push width info down into child items
    publishInnerWidth: function(ownerContext, outerWidth) {
        // If we are acting as a container, publish the innerWidth for the ColumnLayout to use
        if (!ownerContext.hasRawContent) {
            ownerContext.setProp('innerWidth', outerWidth - ownerContext.getBorderInfo().width, false);
        }
    },

    // Push content height outwards when we are shrinkwrapping
    calculateOwnerHeightFromContentHeight: function (ownerContext, contentHeight) {
        var result = this.callParent(arguments);

        // If we are NOT a group header, we just use the auto component's measurement
        if (!ownerContext.hasRawContent) {
            if (this.owner.noWrap || ownerContext.hasDomProp('width')) {
                return contentHeight + this.owner.titleEl.getHeight() + ownerContext.getBorderInfo().height;
            }

            // We do not have the information to return the height yet because we cannot know
            // the final height of the text el
            return null;
        }
        return result;
    },

    // Push content width outwards when we are shrinkwrapping
    calculateOwnerWidthFromContentWidth: function (ownerContext, contentWidth) {
        var owner = this.owner,
            padWidth = ownerContext.getPaddingInfo().width,
            triggerOffset = this.getTriggerOffset(owner, ownerContext),
            inner;
            
        // Only measure the content if we're not grouped, otherwise
        // the size should be governed by the children
        if (owner.isGroupHeader) {
            inner = contentWidth;
        } else {
            inner = Math.max(contentWidth, owner.textEl.getWidth() + ownerContext.titleContext.getPaddingInfo().width);
        }
        return inner + padWidth + triggerOffset;
    },
    
    getTriggerOffset: function(owner, ownerContext) {
        var width = 0;
        if (ownerContext.widthModel.shrinkWrap && !owner.menuDisabled) {
            // If we have any children underneath, then we already have space reserved
            if (owner.query('>:not([hidden])').length === 0) {
                width = owner.self.triggerElWidth;
            }
        }
        return width;
    }
});

/**
 * @private
 *
 * This class is used only by the grid's HeaderContainer docked child.
 *
 * It adds the ability to shrink the vertical size of the inner container element back if a grouped
 * column header has all its child columns dragged out, and the whole HeaderContainer needs to shrink back down.
 *
 * Also, after every layout, after all headers have attained their 'stretchmax' height, it goes through and calls
 * `setPadding` on the columns so that they lay out correctly.
 */
Ext.define('Ext.grid.ColumnLayout', {
    extend:  Ext.layout.container.HBox ,
    alias: 'layout.gridcolumn',
    type : 'gridcolumn',

    reserveOffset: false,

    firstHeaderCls: Ext.baseCSSPrefix + 'column-header-first',
    lastHeaderCls: Ext.baseCSSPrefix + 'column-header-last',

    initLayout: function() {
        var me = this;
        if (me.scrollbarWidth === undefined) {
            me.self.prototype.scrollbarWidth = Ext.getScrollbarSize().width;
        }
        me.grid = this.owner.up('[scrollerOwner]');
        me.callParent();
    },

    // Collect the height of the table of data upon layout begin
    beginLayout: function (ownerContext) {
        var me = this,
            owner = me.owner,
            grid = me.grid,
            view = grid.view,
            items = me.getVisibleItems(),
            len = items.length,
            firstCls = me.firstHeaderCls, 
            lastCls = me.lastHeaderCls,
            removeCls = [firstCls, lastCls],
            i, item;

        // If we are one side of a locking grid, then if we are on the "normal" side, we have to grab the normal view
        // for use in determining whether to subtract scrollbar width from available width.
        // The locked side does not have scrollbars, so it should not look at the view.
        if (grid.lockable) {
            if (owner.up('tablepanel') === view.normalGrid) {
                view = view.normalGrid.getView();
            } else {
                view = null;
            }
        }

        for (i = 0; i < len; i++) {
            item = items[i];
            item.removeCls(removeCls);
            if (i === 0) {
                item.addCls(firstCls);
            }

            if (i === len - 1) {
                item.addCls(lastCls);
            }
        }

        me.callParent(arguments);

        // If the owner is the grid's HeaderContainer, and the UI displays old fashioned scrollbars and there is a rendered View with data in it,
        // collect the View context to interrogate it for overflow, and possibly invalidate it if there is overflow
        if (!owner.isColumn && me.scrollbarWidth && !grid.collapsed && view &&
                view.rendered && (ownerContext.viewTable = view.body.dom)) {
            ownerContext.viewContext = ownerContext.context.getCmp(view);
        }
    },

    roundFlex: function(width) {
        return Math.floor(width);
    },

    calculate: function(ownerContext) {
        this.callParent(arguments);

        if (ownerContext.state.parallelDone) {
            // TODO: auto width columns aren't necessarily done here.
            // see view.TableLayout, there is a work around for that there 
            ownerContext.setProp('columnWidthsDone', true);
        }

        // Collect the height of the data table if we need it to determine overflow
        if (ownerContext.viewContext) {
            ownerContext.state.tableHeight = ownerContext.viewTable.offsetHeight;
        }
    },

    completeLayout: function(ownerContext) {
        var me = this,
            owner = me.owner,
            state = ownerContext.state;

        me.callParent(arguments);

        // If we have not been through this already, and the owning Container is configured
        // forceFit, is not a group column and and there is a valid width, then convert
        // widths to flexes, and loop back.
        if (!ownerContext.flexedItems.length && !state.flexesCalculated && owner.forceFit &&

            // Recalculate based upon all columns now being flexed instead of sized.
            // Set flag, so that we do not do this infinitely
            me.convertWidthsToFlexes(ownerContext)) {
            me.cacheFlexes(ownerContext);
            ownerContext.invalidate({
                state: {
                    flexesCalculated: true
                }
            });
        } else {
            ownerContext.setProp('columnWidthsDone', true);
        }
    },

    convertWidthsToFlexes: function(ownerContext) {
        var me = this,
            totalWidth = 0,
            calculated = me.sizeModels.calculated,
            childItems, len, i, childContext, item;

        childItems = ownerContext.childItems;
        len = childItems.length;

        for (i = 0; i < len; i++) {
            childContext = childItems[i];
            item = childContext.target;

            totalWidth += childContext.props.width;

            // Only allow to be flexed if it's a resizable column
            if (!(item.fixed || item.resizable === false)) {

                // For forceFit, just use allocated width as the flex value, and the proportions
                // will end up the same whatever HeaderContainer width they are being forced into.
                item.flex = ownerContext.childItems[i].flex = childContext.props.width;
                item.width = null;
                childContext.widthModel = calculated;
            }
        }

        // Only need to loop back if the total column width is not already an exact fit
        return totalWidth !== ownerContext.props.width;
    },

    /**
     * @private
     * Local getContainerSize implementation accounts for vertical scrollbar in the view.
     */
    getContainerSize: function(ownerContext) {
        var me = this,
            result,
            viewContext = ownerContext.viewContext,
            viewHeight;

        // Column, NOT the main grid's HeaderContainer
        if (me.owner.isColumn) {
            result = me.getColumnContainerSize(ownerContext);
        }

        // This is the maingrid's HeaderContainer
        else {
            result = me.callParent(arguments);

            // If we've collected a viewContext and we're not shrinkwrapping the height
            // then we see if we have to narrow the width slightly to account for scrollbar
            if (viewContext && !viewContext.heightModel.shrinkWrap &&
                    viewContext.target.componentLayout.ownerContext) { // if (its layout is running)
                viewHeight = viewContext.getProp('height');
                if (isNaN(viewHeight)) {
                    me.done = false;
                } else if (ownerContext.state.tableHeight > viewHeight) {
                    result.width -= me.scrollbarWidth;
                    ownerContext.state.parallelDone = false;
                    viewContext.invalidate();
                }
            }
        }

// TODO - flip the initial assumption to "we have a vscroll" to avoid the invalidate in most
// cases (and the expensive ones to boot)

        return result;
    },

    getColumnContainerSize : function(ownerContext) {
        var padding = ownerContext.paddingContext.getPaddingInfo(),
            got = 0,
            needed = 0,
            gotWidth, gotHeight, width, height;

        // In an shrinkWrap width/height case, we must not ask for any of these dimensions
        // because they will be determined by contentWidth/Height which is calculated by
        // this layout...

        // Fit/Card layouts are able to set just the width of children, allowing child's
        // resulting height to autosize the Container.
        // See examples/tabs/tabs.html for an example of this.

        if (!ownerContext.widthModel.shrinkWrap) {
            ++needed;
            width = ownerContext.getProp('innerWidth');
            gotWidth = (typeof width == 'number');
            if (gotWidth) {
                ++got;
                width -= padding.width;
                if (width < 0) {
                    width = 0;
                }
            }
        }

        if (!ownerContext.heightModel.shrinkWrap) {
            ++needed;
            height = ownerContext.getProp('innerHeight');
            gotHeight = (typeof height == 'number');
            if (gotHeight) {
                ++got;
                height -= padding.height;
                if (height < 0) {
                    height = 0;
                }
            }
        }

        return {
            width: width,
            height: height,
            needed: needed,
            got: got,
            gotAll: got == needed,
            gotWidth: gotWidth,
            gotHeight: gotHeight
        };
    },

    // FIX: when flexing we actually don't have enough space as we would
    // typically because of the scrollOffset on the GridView, must reserve this
    publishInnerCtSize: function(ownerContext) {
        var me = this,
            size = ownerContext.state.boxPlan.targetSize,
            cw = ownerContext.peek('contentWidth'),
            view;

        // Allow the other co-operating objects to know whether the columns overflow the available width.
        me.owner.tooNarrow = ownerContext.state.boxPlan.tooNarrow;

        // InnerCt MUST stretch to accommodate all columns so that left/right scrolling is enabled in the header container.
        if ((cw != null) && !me.owner.isColumn) {
            size.width = cw;

            // innerCt must also encompass any vertical scrollbar width if there may be one
            view = me.owner.ownerCt.view;
            if (view.scrollFlags.y) {
                size.width += me.scrollbarWidth;
            }
        }

        return me.callParent(arguments);
    }
});

/**
 * @private
 *
 * Manages and provides information about a TablePanel's *visible leaf* columns.
 */
Ext.define('Ext.grid.ColumnManager', {
    alternateClassName: ['Ext.grid.ColumnModel'],

    columns: null,

    constructor: function(visibleOnly, headerCt, secondHeaderCt) {
        this.headerCt = headerCt;

        // We are managing columns for a lockable grid...
        if (secondHeaderCt) {
            this.secondHeaderCt = secondHeaderCt;
        }
        this.visibleOnly = !!visibleOnly;
    },

    getColumns: function() {
        if (!this.columns) {
            this.cacheColumns();
        }
        return this.columns;
    },

    /**
     * If called from a root header, returns the index of a leaf level header regardless of what the nesting
     * structure is.
     *
     * If called from a group header, returns the index of a leaf level header relative to the group header.
     *
     * If a group header is passed, the index of the first leaf level header within it is returned.
     *
     * @param {Ext.grid.column.Column} header The header to find the index of
     * @return {Number} The index of the specified column header
     */
    getHeaderIndex: function (header) {
        if (header.isGroupHeader) {
            // Get the first header for the particular group header. The .getHeaderColumns API
            // will sort out if it's to be just visible columns or all columns.
            header = this.getHeaderColumns(header)[0];
        }

        return Ext.Array.indexOf(this.getColumns(), header);
    },

    /**
     * If called from a root header, gets a leaf level header by index regardless of what the nesting
     * structure is.
     *
     * If called from a group header, returns the index of a leaf level header relative to the group header.
     *
     * @param {Number} index The column index for which to retrieve the column.
     * @return {Ext.grid.column.Column} The header. `null` if it doesn't exist.
     */
    getHeaderAtIndex: function(index) {
        var columns = this.getColumns(),
            col = columns[index];
            
        return col || null;
    },
    
    getPreviousSibling: function(header){
        var index = this.getHeaderIndex(header),
            col = null;
            
        if (index > 0) {
            col = this.getColumns()[index - 1];
        }
        return col;
    },
    
    getNextSibling: function(header){
        var index = this.getHeaderIndex(header),
            col;
            
        if (index !== -1) {
            col = this.getColumns()[index + 1];
        }
        return col || null;
    },
    
    /**
     * Get the first column.
     * @return {Ext.grid.column.Column} The header. `null` if it doesn't exist
     */
    getFirst: function(){
        var columns = this.getColumns();
        return columns.length > 0 ? columns[0] : null;
    },
    
    /**
     * Get the last column.
     * @return {Ext.grid.column.Column} The header. `null` if it doesn't exist
     */
    getLast: function(){
        var columns = this.getColumns(),
            len = columns.length;
            
        return len > 0 ? columns[len - 1] : null;
    },
    
    /**
     * Get a leaf level header by index regardless of what the nesting
     * structure is.
     * @param {String} id The id
     * @return {Ext.grid.column.Column} The header. `null` if it doesn't exist.
     */
    getHeaderById: function(id) {
        var columns = this.getColumns(),
            len = columns.length,
            i, header;
            
        for (i = 0; i < len; ++i) {
            header = columns[i];
            if (header.getItemId() === id) {
                return header;
            }
        }
        return null;
    },

    /**
     * When passed a column index, returns the closet *visible* column to that. If the column at the passed index is visible,
     * that is returned. If it is hidden, either the next visible, or the previous visible column is returned.
     *
     * If called from a group header, returns the visible index of a leaf level header relative to the group header with the
     * same stipulations as outlined above.
     *
     * @param {Number} index Position at which to find the closest visible column.
     */
    getVisibleHeaderClosestToIndex: function(index) {
        var result = this.getHeaderAtIndex(index);
        if (result && result.hidden) {
            result = result.next(':not([hidden])') || result.prev(':not([hidden])');
        }
        return result;
    },

    cacheColumns: function() {
        var columns = this.getHeaderColumns(this.headerCt),
            second = this.secondHeaderCt;
            
        if (second) {
            columns = columns.concat(this.getHeaderColumns(second));
        }
        this.columns = columns;
    },
    
    getHeaderColumns: function(header) {
        var result = this.visibleOnly ? header.getVisibleGridColumns() : header.getGridColumns();
        return Ext.Array.clone(result);
    },

    invalidate: function() {
        var root = this.rootColumns;
        this.columns = null;

        // If we are part of a lockable assembly, invalidate the root column manager
        if (root) {
            root.invalidate();
        }
    },
    
    destroy: function(){
        this.columns = this.rootColumns = null;
    }
}, function() {
    this.createAlias('indexOf', 'getHeaderIndex');
});

/**
 * This is a base class for layouts that contain a single item that automatically expands to fill the layout's
 * container. This class is intended to be extended or created via the layout:'fit'
 * {@link Ext.container.Container#layout} config, and should generally not need to be created directly via the new keyword.
 *
 * Fit layout does not have any direct config options (other than inherited ones). To fit a panel to a container using
 * Fit layout, simply set `layout: 'fit'` on the container and add a single panel to it.
 *
 *     @example
 *     Ext.create('Ext.panel.Panel', {
 *         title: 'Fit Layout',
 *         width: 300,
 *         height: 150,
 *         layout:'fit',
 *         items: {
 *             title: 'Inner Panel',
 *             html: 'This is the inner panel content',
 *             bodyPadding: 20,
 *             border: false
 *         },
 *         renderTo: Ext.getBody()
 *     });
 *
 * If the container has multiple items, all of the items will all be equally sized. This is usually not
 * desired, so to avoid this, place only a **single** item in the container. This sizing of all items
 * can be used to provide a background {@link Ext.Img image} that is "behind" another item
 * such as a {@link Ext.view.View dataview} if you also absolutely position the items.
 */
Ext.define('Ext.layout.container.Fit', {

    /* Begin Definitions */
    extend:  Ext.layout.container.Container ,
    alternateClassName: 'Ext.layout.FitLayout',

    alias: 'layout.fit',

    /* End Definitions */

    itemCls: Ext.baseCSSPrefix + 'fit-item',
    targetCls: Ext.baseCSSPrefix + 'layout-fit',
    type: 'fit',
   
    /**
     * @cfg {Object} defaultMargins
     * If the individual contained items do not have a margins property specified or margin specified via CSS, the
     * default margins from this property will be applied to each item.
     *
     * This property may be specified as an object containing margins to apply in the format:
     *
     *     {
     *         top: (top margin),
     *         right: (right margin),
     *         bottom: (bottom margin),
     *         left: (left margin)
     *     }
     *
     * This property may also be specified as a string containing space-separated, numeric margin values. The order of
     * the sides associated with each value matches the way CSS processes margin values:
     *
     *   - If there is only one value, it applies to all sides.
     *   - If there are two values, the top and bottom borders are set to the first value and the right and left are
     *     set to the second.
     *   - If there are three values, the top is set to the first value, the left and right are set to the second,
     *     and the bottom is set to the third.
     *   - If there are four values, they apply to the top, right, bottom, and left, respectively.
     *
     */
    defaultMargins: {
        top: 0,
        right: 0,
        bottom: 0,
        left: 0
    },

    manageMargins: true,

    sizePolicies: {
        0: { readsWidth: 1, readsHeight: 1, setsWidth: 0, setsHeight: 0 },
        1: { readsWidth: 0, readsHeight: 1, setsWidth: 1, setsHeight: 0 },
        2: { readsWidth: 1, readsHeight: 0, setsWidth: 0, setsHeight: 1 },
        3: { readsWidth: 0, readsHeight: 0, setsWidth: 1, setsHeight: 1 }
    },

    getItemSizePolicy: function (item, ownerSizeModel) {
        // this layout's sizePolicy is derived from its owner's sizeModel:
        var sizeModel = ownerSizeModel || this.owner.getSizeModel(),
            mode = (sizeModel.width.shrinkWrap ? 0 : 1) |
                   (sizeModel.height.shrinkWrap ? 0 : 2);

       return this.sizePolicies[mode];
    },

    beginLayoutCycle: function (ownerContext, firstCycle) {
        var me = this,
            // determine these before the lastSizeModels get updated:
            resetHeight = me.lastHeightModel && me.lastHeightModel.calculated,
            resetWidth = me.lastWidthModel && me.lastWidthModel.calculated,
            resetSizes = resetWidth || resetHeight,
            maxChildMinHeight = 0, maxChildMinWidth = 0,
            c, childItems, i, item, length, margins, minHeight, minWidth, style, undef;

        me.callParent(arguments);

        // Clear any dimensions which we set before calculation, in case the current
        // settings affect the available size. This particularly effects self-sizing
        // containers such as fields, in which the target element is naturally sized,
        // and should not be stretched by a sized child item.
        if (resetSizes && ownerContext.targetContext.el.dom.tagName.toUpperCase() != 'TD') {
            resetSizes = resetWidth = resetHeight = false;
        }

        childItems = ownerContext.childItems;
        length = childItems.length;

        for (i = 0; i < length; ++i) {
            item = childItems[i];

            // On the firstCycle, we determine the max of the minWidth/Height of the items
            // since these can cause the container to grow scrollbars despite our attempts
            // to fit the child to the container.
            if (firstCycle) {
                c = item.target;
                minHeight = c.minHeight;
                minWidth = c.minWidth;

                if (minWidth || minHeight) {
                    margins = item.marginInfo || item.getMarginInfo();
                    // if the child item has undefined minWidth/Height, these will become
                    // NaN by adding the margins...
                    minHeight += margins.height;
                    minWidth += margins.height;

                    // if the child item has undefined minWidth/Height, these comparisons
                    // will evaluate to false... that is, "0 < NaN" == false...
                    if (maxChildMinHeight < minHeight) {
                        maxChildMinHeight = minHeight;
                    }
                    if (maxChildMinWidth < minWidth) {
                        maxChildMinWidth = minWidth;
                    }
                }
            }

            if (resetSizes) {
                style = item.el.dom.style;

                if (resetHeight) {
                    style.height = '';
                }
                if (resetWidth) {
                    style.width = '';
                }
            }
        }

        if (firstCycle) {
            ownerContext.maxChildMinHeight = maxChildMinHeight;
            ownerContext.maxChildMinWidth = maxChildMinWidth;
        }

        // Cache the overflowX/Y flags, but make them false in shrinkWrap mode (since we
        // won't be triggering overflow in that case) and false if we have no minSize (so
        // no child to trigger an overflow).
        c = ownerContext.target;
        ownerContext.overflowX = (!ownerContext.widthModel.shrinkWrap && 
                                   ownerContext.maxChildMinWidth &&
                                   c.scrollFlags.x) || undef;

        ownerContext.overflowY = (!ownerContext.heightModel.shrinkWrap &&
                                   ownerContext.maxChildMinHeight &&
                                   c.scrollFlags.y) || undef;
    },

    calculate : function (ownerContext) {
        var me = this,
            childItems = ownerContext.childItems,
            length = childItems.length,
            containerSize = me.getContainerSize(ownerContext),
            info = {
                length: length,
                ownerContext: ownerContext,
                targetSize: containerSize
            },
            shrinkWrapWidth = ownerContext.widthModel.shrinkWrap,
            shrinkWrapHeight = ownerContext.heightModel.shrinkWrap,
            overflowX = ownerContext.overflowX,
            overflowY = ownerContext.overflowY,
            scrollbars, scrollbarSize, padding, i, contentWidth, contentHeight;

        if (overflowX || overflowY) {
            // If we have children that have minHeight/Width, we may be forced to overflow
            // and gain scrollbars. If so, we want to remove their space from the other
            // axis so that we fit things inside the scrollbars rather than under them.
            scrollbars = me.getScrollbarsNeeded(
                    overflowX && containerSize.width, overflowY && containerSize.height,
                    ownerContext.maxChildMinWidth, ownerContext.maxChildMinHeight);

            if (scrollbars) {
                scrollbarSize = Ext.getScrollbarSize();
                if (scrollbars & 1) { // if we need the hscrollbar, remove its height
                    containerSize.height -= scrollbarSize.height;
                }
                if (scrollbars & 2) { // if we need the vscrollbar, remove its width
                    containerSize.width -= scrollbarSize.width;
                }
            }
        }

        // Size the child items to the container (if non-shrinkWrap):
        for (i = 0; i < length; ++i) {
            info.index = i;
            me.fitItem(childItems[i], info);
        }
        
        if (shrinkWrapHeight || shrinkWrapWidth) {
            padding = ownerContext.targetContext.getPaddingInfo();
            
            if (shrinkWrapWidth) {
                if (overflowY && !containerSize.gotHeight) {
                    // if we might overflow vertically and don't have the container height,
                    // we don't know if we will need a vscrollbar or not, so we must wait
                    // for that height so that we can determine the contentWidth...
                    me.done = false;
                } else {
                    contentWidth = info.contentWidth + padding.width;
                    // the scrollbar flag (if set) will indicate that an overflow exists on
                    // the horz(1) or vert(2) axis... if not set, then there could never be
                    // an overflow...
                    if (scrollbars & 2) { // if we need the vscrollbar, add its width
                        contentWidth += scrollbarSize.width;
                    }
                    if (!ownerContext.setContentWidth(contentWidth)) {
                        me.done = false;
                    }
                }
            }

            if (shrinkWrapHeight) {
                if (overflowX && !containerSize.gotWidth) {
                    // if we might overflow horizontally and don't have the container width,
                    // we don't know if we will need a hscrollbar or not, so we must wait
                    // for that width so that we can determine the contentHeight...
                    me.done = false;
                } else {
                    contentHeight = info.contentHeight + padding.height;
                    // the scrollbar flag (if set) will indicate that an overflow exists on
                    // the horz(1) or vert(2) axis... if not set, then there could never be
                    // an overflow...
                    if (scrollbars & 1) { // if we need the hscrollbar, add its height
                        contentHeight += scrollbarSize.height;
                    }
                    if (!ownerContext.setContentHeight(contentHeight)) {
                        me.done = false;
                    }
                }
            }
        }
    },

    fitItem: function (itemContext, info) {
        var me = this;

        if (itemContext.invalid) {
            me.done = false;
            return;
        }

        info.margins = itemContext.getMarginInfo();
        info.needed = info.got = 0;

        me.fitItemWidth(itemContext, info);
        me.fitItemHeight(itemContext, info);

        // If not all required dimensions have been satisfied, we're not done.
        if (info.got != info.needed) {
            me.done = false;
        }
    },

    fitItemWidth: function (itemContext, info) {
        var contentWidth, width;
        // Attempt to set only dimensions that are being controlled, not shrinkWrap dimensions
        if (info.ownerContext.widthModel.shrinkWrap) {
            // contentWidth must include the margins to be consistent with setItemWidth
            width = itemContext.getProp('width') + info.margins.width;
            // because we add margins, width will be NaN or a number (not undefined)

            contentWidth = info.contentWidth;
            if (contentWidth === undefined) {
                info.contentWidth = width;
            } else {
                info.contentWidth = Math.max(contentWidth, width);
            }
        } else if (itemContext.widthModel.calculated) {
            ++info.needed;
            if (info.targetSize.gotWidth) {
                ++info.got;
                this.setItemWidth(itemContext, info);
            }
        }

        this.positionItemX(itemContext, info);
    },

    fitItemHeight: function (itemContext, info) {
        var contentHeight, height;
        if (info.ownerContext.heightModel.shrinkWrap) {
            // contentHeight must include the margins to be consistent with setItemHeight
            height = itemContext.getProp('height') + info.margins.height;
            // because we add margins, height will be NaN or a number (not undefined)

            contentHeight = info.contentHeight;
            if (contentHeight === undefined) {
                info.contentHeight = height;
            } else {
                info.contentHeight = Math.max(contentHeight, height);
            }
        } else if (itemContext.heightModel.calculated) {
            ++info.needed;
            if (info.targetSize.gotHeight) {
                ++info.got;
                this.setItemHeight(itemContext, info);
            }
        }

        this.positionItemY(itemContext, info);
    },

    positionItemX: function (itemContext, info) {
        var margins = info.margins;

        // Adjust position to account for configured margins or if we have multiple items
        // (all items should overlap):
        if (info.index || margins.left) {
            itemContext.setProp('x', margins.left);
        }

        if (margins.width) {
            // Need the margins for shrink-wrapping but old IE sometimes collapses the left margin into the padding
            itemContext.setProp('margin-right', margins.width);
        }
    },

    positionItemY: function (itemContext, info) {
        var margins = info.margins;

        if (info.index || margins.top) {
            itemContext.setProp('y', margins.top);
        }

        if (margins.height) {
            // Need the margins for shrink-wrapping but old IE sometimes collapses the top margin into the padding
            itemContext.setProp('margin-bottom', margins.height);
        }
    },

    setItemHeight: function (itemContext, info) {
        itemContext.setHeight(info.targetSize.height - info.margins.height);
    },

    setItemWidth: function (itemContext, info) {
        itemContext.setWidth(info.targetSize.width - info.margins.width);
    }
});

/**
 * @author Nicolas Ferrero
 *
 * TablePanel is the basis of both {@link Ext.tree.Panel TreePanel} and {@link Ext.grid.Panel GridPanel}.
 *
 * TablePanel aggregates:
 *
 *  - a Selection Model
 *  - a View
 *  - a Store
 *  - Scrollers
 *  - Ext.grid.header.Container
 * 
 * @mixins Ext.grid.locking.Lockable
 */
Ext.define('Ext.panel.Table', {
    extend:  Ext.panel.Panel ,

    alias: 'widget.tablepanel',
    
               
                                  
      

           
                                 
                                  
                                      
                                           
                                    
                                   
      

    extraBaseCls: Ext.baseCSSPrefix + 'grid',
    extraBodyCls: Ext.baseCSSPrefix + 'grid-body',

    layout: 'fit',
    /**
     * @property {Boolean} hasView
     * True to indicate that a view has been injected into the panel.
     */
    hasView: false,

    // each panel should dictate what viewType and selType to use
    /**
     * @cfg {String} viewType
     * An xtype of view to use. This is automatically set to 'gridview' by {@link Ext.grid.Panel Grid}
     * and to 'treeview' by {@link Ext.tree.Panel Tree}.
     * @protected
     */
    viewType: null,

    /**
     * @cfg {Object} viewConfig
     * A config object that will be applied to the grid's UI view. Any of the config options available for
     * {@link Ext.view.Table} can be specified here. This option is ignored if {@link #view} is specified.
     */

    /**
     * @cfg {Ext.view.Table} view
     * The {@link Ext.view.Table} used by the grid. Use {@link #viewConfig} to just supply some config options to
     * view (instead of creating an entire View instance).
     */

    /**
     * @cfg {String} selType
     * An xtype of selection model to use. Defaults to 'rowmodel'. This is used to create selection model if just
     * a config object or nothing at all given in {@link #selModel} config.
     */
    selType: 'rowmodel',

    /**
     * @cfg {Ext.selection.Model/Object} selModel
     * A {@link Ext.selection.Model selection model} instance or config object.  In latter case the {@link #selType}
     * config option determines to which type of selection model this config is applied.
     */

    /**
     * @cfg {Boolean} [multiSelect=false]
     * True to enable 'MULTI' selection mode on selection model.
     * @deprecated 4.1.1 Use {@link Ext.selection.Model#mode} 'MULTI' instead.
     */

    /**
     * @cfg {Boolean} [simpleSelect=false]
     * True to enable 'SIMPLE' selection mode on selection model.
     * @deprecated 4.1.1 Use {@link Ext.selection.Model#mode} 'SIMPLE' instead.
     */

    /**
     * @cfg {Ext.data.Store} store (required)
     * The {@link Ext.data.Store Store} the grid should use as its data source.
     */

    /**
     * @cfg {String/Boolean} scroll
     * Scrollers configuration. Valid values are 'both', 'horizontal' or 'vertical'.
     * True implies 'both'. False implies 'none'.
     */
    scroll: true,

    /**
     * @cfg {Ext.grid.column.Column[]/Object} columns
     * An array of {@link Ext.grid.column.Column column} definition objects which define all columns that appear in this
     * grid. Each column definition provides the header text for the column, and a definition of where the data for that
     * column comes from.
     *
     * This can also be a configuration object for a {Ext.grid.header.Container HeaderContainer} which may override
     * certain default configurations if necessary. For example, the special layout may be overridden to use a simpler
     * layout, or one can set default values shared by all columns:
     * 
     *     columns: {
     *         items: [
     *             {
     *                 text: "Column A"
     *                 dataIndex: "field_A"
     *             },{
     *                 text: "Column B",
     *                 dataIndex: "field_B"
     *             }, 
     *             ...
     *         ],
     *         defaults: {
     *             flex: 1
     *         }
     *     }
     */

    /**
     * @cfg {Boolean} forceFit
     * True to force the columns to fit into the available width. Headers are first sized according to configuration,
     * whether that be a specific width, or flex. Then they are all proportionally changed in width so that the entire
     * content width is used. For more accurate control, it is more optimal to specify a flex setting on the columns
     * that are to be stretched & explicit widths on columns that are not.
     */

    /**
     * @cfg {Ext.grid.feature.Feature[]/Object[]/Ext.enums.Feature[]} features
     * An array of grid Features to be added to this grid. Can also be just a single feature instead of array.
     *
     * Features config behaves much like {@link #plugins}.
     * A feature can be added by either directly referencing the instance:
     *
     *     features: [Ext.create('Ext.grid.feature.GroupingSummary', {groupHeaderTpl: 'Subject: {name}'})],
     *
     * By using config object with ftype:
     *
     *     features: [{ftype: 'groupingsummary', groupHeaderTpl: 'Subject: {name}'}],
     *
     * Or with just a ftype:
     *
     *     features: ['grouping', 'groupingsummary'],
     *
     * See {@link Ext.enums.Feature} for list of all ftypes.
     */

    /**
     * @cfg {Boolean} [hideHeaders=false]
     * True to hide column headers.
     */

    /**
     * @cfg {Boolean} deferRowRender
     * Defaults to true to enable deferred row rendering.
     *
     * This allows the View to execute a refresh quickly, with the expensive update of the row structure deferred so
     * that layouts with GridPanels appear, and lay out more quickly.
     */

    /**
     * @cfg {Object} verticalScroller
     * A config object to be used when configuring the {@link Ext.grid.plugin.BufferedRenderer scroll monitor} to control
     * refreshing of data in an "infinite grid".
     * 
     * Configurations of this object allow fine tuning of data caching which can improve performance and usability
     * of the infinite grid.
     */

    deferRowRender: true,
     
    /**
     * @cfg {Boolean} sortableColumns
     * False to disable column sorting via clicking the header and via the Sorting menu items.
     */
    sortableColumns: true,

    /**
     * @cfg {Boolean} [enableLocking=false]
     * Configure as `true` to enable locking support for this grid. Alternatively, locking will also be automatically
     * enabled if any of the columns in the {@link #columns columns} configuration contain a {@link Ext.grid.column.Column#locked locked} config option.
     * 
     * A locking grid is processed in a special way. The configuration options are cloned and *two* grids are created to be the locked (left) side
     * and the normal (right) side. This Panel becomes merely a {@link Ext.container.Container container} which arranges both in an {@link Ext.layout.container.HBox HBox} layout.
     * 
     * {@link #plugins Plugins} may be targeted at either locked, or unlocked grid, or, both, in which case the plugin is cloned and used on both sides.
     * 
     * Plugins may also be targeted at the containing locking Panel.
     * 
     * This is configured by specifying a `lockableScope` property in your plugin which may have the following values:
     * 
     *  * `"both"` (the default) - The plugin is added to both grids
     *  * `"top"` - The plugin is added to the containing Panel
     *  * `"locked"` - The plugin is added to the locked (left) grid
     *  * `"normal"` - The plugin is added to the normal (right) grid
     *
     * If `both` is specified, then each copy of the plugin gains a property `lockingPartner` which references its sibling on the other side so that they
     * can synchronize operations is necessary.
     * 
     * {@link #features Features} may also be configured with `lockableScope` and may target the locked grid, the normal grid or both grids. Features
     * also get a `lockingPartner` reference injected.
     */
    enableLocking: false,

    // private property used to determine where to go down to find views
    // this is here to support locking.
    scrollerOwner: true,

    /**
     * @cfg {Boolean} [enableColumnMove=true]
     * False to disable column dragging within this grid.
     */
    enableColumnMove: true,
    
    /**
     * @cfg {Boolean} [sealedColumns=false]
     * True to constrain column dragging so that a column cannot be dragged in or out of it's
     * current group. Only relevant while {@link #enableColumnMove} is enabled.
     */
    sealedColumns: false,

    /**
     * @cfg {Boolean} [enableColumnResize=true]
     * False to disable column resizing within this grid.
     */
    enableColumnResize: true,

    /**
     * @cfg {Boolean} [enableColumnHide=true]
     * False to disable column hiding within this grid.
     */

    /**
     * @cfg {Boolean} columnLines Adds column line styling
     */

    /**
     * @cfg {Boolean} [rowLines=true] Adds row line styling
     */
    rowLines: true,

    /**
     * @cfg {Boolean} [disableSelection=false]
     * True to disable selection model.
     */

    /**
     * @cfg {String} emptyText Default text (html tags are accepted) to display in the Panel body when the Store
     * is empty. When specified, and the Store is empty, the text will be rendered inside a DIV with the CSS class "x-grid-empty".
     */
    
    /**
     * @cfg {Boolean} [allowDeselect=false]
     * True to allow deselecting a record. This config is forwarded to {@link Ext.selection.Model#allowDeselect}.
     */

    /**
     * @property {Boolean} optimizedColumnMove
     * If you are writing a grid plugin or a {Ext.grid.feature.Feature Feature} which creates a column-based structure which
     * needs a view refresh when columns are moved, then set this property in the grid.
     *
     * An example is the built in {@link Ext.grid.feature.AbstractSummary Summary} Feature. This creates summary rows, and the
     * summary columns must be in the same order as the data columns. This plugin sets the `optimizedColumnMove` to `false.
     */
    
    colLinesCls: Ext.baseCSSPrefix + 'grid-with-col-lines',
    rowLinesCls: Ext.baseCSSPrefix + 'grid-with-row-lines',
    noRowLinesCls: Ext.baseCSSPrefix + 'grid-no-row-lines',
    hiddenHeaderCtCls: Ext.baseCSSPrefix + 'grid-header-ct-hidden',
    hiddenHeaderCls: Ext.baseCSSPrefix + 'grid-header-hidden',
    resizeMarkerCls: Ext.baseCSSPrefix + 'grid-resize-marker',
    emptyCls: Ext.baseCSSPrefix + 'grid-empty',

    initComponent: function() {

        var me          = this,
            headerCtCfg = me.columns || me.colModel,
            view,
            i, len,
            // Look up the configured Store. If none configured, use the fieldless, empty Store defined in Ext.data.Store.
            store       = me.store = Ext.data.StoreManager.lookup(me.store || 'ext-empty-store'),
            columns;

        // Add the row/column line classes to the body element so that the settings are not inherited by docked grids (https://sencha.jira.com/browse/EXTJSIV-9263).
        if (me.columnLines) {
            me.addBodyCls(me.colLinesCls);
        }

        me.addBodyCls(me.rowLines ? me.rowLinesCls : me.noRowLinesCls);
        me.addBodyCls(me.extraBodyCls);


        // The columns/colModel config may be either a fully instantiated HeaderContainer, or an array of Column definitions, or a config object of a HeaderContainer
        // Either way, we extract a columns property referencing an array of Column definitions.
        if (headerCtCfg.isRootHeader) {
            me.headerCt = headerCtCfg;

            me.headerCt.forceFit = !!me.forceFit;

            // If it's an instance then the column managers were already created and bound to the headerCt.
            me.columnManager = headerCtCfg.columnManager;
            me.visibleColumnManager = headerCtCfg.visibleColumnManager;
        } else {

            // If any of the Column objects contain a locked property, and are not processed, this is a lockable TablePanel, a
            // special view will be injected by the Ext.grid.locking.Lockable mixin, so no processing of .
            if (me.enableLocking || me.hasLockedColumns(headerCtCfg)) {
                me.self.mixin('lockable', Ext.grid.locking.Lockable);
                me.injectLockable();
            }
            // Not lockable - create the HeaderContainer
            else {
                if (Ext.isArray(headerCtCfg)) {
                    headerCtCfg = {
                        items: headerCtCfg
                    };
                }
                Ext.apply(headerCtCfg, {
                    grid: me,
                    forceFit: me.forceFit,
                    sortable: me.sortableColumns,
                    enableColumnMove: me.enableColumnMove,
                    enableColumnResize: me.enableColumnResize,
                    sealed: me.sealedColumns
                });

                if (Ext.isDefined(me.enableColumnHide)) {
                    headerCtCfg.enableColumnHide = me.enableColumnHide;
                }

                // Create our HeaderCOntainer from the generated configuration
                if (!me.headerCt) {
                    me.headerCt = new Ext.grid.header.Container(headerCtCfg);
                }
            }
        }

        // Maintain backward compatibiliy by providing the initial leaf column set as a property.
        me.columns = columns = me.headerCt.getGridColumns();

        me.scrollTask = new Ext.util.DelayedTask(me.syncHorizontalScroll, me);

        me.addEvents(
            // documented on GridPanel
            'reconfigure',
            /**
             * @event viewready
             * Fires when the grid view is available (use this for selecting a default row).
             * @param {Ext.panel.Table} this
             */
            'viewready'
        );

        me.cls = me.cls || '' + (' ' + me.extraBaseCls);

        // autoScroll is not a valid configuration
        delete me.autoScroll;

        // If this TablePanel is lockable (Either configured lockable, or any of the defined columns has a 'locked' property)
        // then a special lockable view containing 2 side-by-side grids will have been injected so we do not need to set up any UI.
        if (!me.hasView) {

            // If the Store is paging blocks of the dataset in, then it can only be sorted remotely.
            if (store.buffered && !store.remoteSort) {
                for (i = 0, len = columns.length; i < len; i++) {
                    columns[i].sortable = false;
                }
            }

            if (me.hideHeaders) {
                me.headerCt.height = 0;
                // don't se the hidden property, we still need these to layout
                me.headerCt.hiddenHeaders = true;
                me.headerCt.addCls(me.hiddenHeaderCtCls);
                me.addCls(me.hiddenHeaderCls);
                // IE Quirks Mode fix
                // If hidden configuration option was used, several layout calculations will be bypassed.
                if (Ext.isIEQuirks) {
                    me.headerCt.style = {
                        display: 'none'
                    };
                }
            }

            me.relayHeaderCtEvents(me.headerCt);
            me.features = me.features || [];
            if (!Ext.isArray(me.features)) {
                me.features = [me.features];
            }
            me.dockedItems = [].concat(me.dockedItems || []);
            me.dockedItems.unshift(me.headerCt);
            me.viewConfig = me.viewConfig || {};

            // AbstractDataView will look up a Store configured as an object
            // getView converts viewConfig into a View instance
            view = me.getView();

            me.items = [view];
            me.hasView = true;

            // Add a listener to synchronize the horizontal scroll position of the headers
            // with the table view's element... Unless we are not showing headers!
            if (!me.hideHeaders) {
                view.on({
                    scroll: {
                        fn: me.onHorizontalScroll,
                        element: 'el',
                        scope: me
                    }
                });
            }

            // Attach this Panel to the Store
            me.bindStore(store, true);

            me.mon(view, {
                viewready: me.onViewReady,
                refresh: me.onRestoreHorzScroll,
                scope: me
            });
        }

        // Relay events from the View whether it be a LockingView, or a regular GridView
        me.relayEvents(me.view, [
            /**
             * @event beforeitemmousedown
             * @inheritdoc Ext.view.View#beforeitemmousedown
             */
            'beforeitemmousedown',
            /**
             * @event beforeitemmouseup
             * @inheritdoc Ext.view.View#beforeitemmouseup
             */
            'beforeitemmouseup',
            /**
             * @event beforeitemmouseenter
             * @inheritdoc Ext.view.View#beforeitemmouseenter
             */
            'beforeitemmouseenter',
            /**
             * @event beforeitemmouseleave
             * @inheritdoc Ext.view.View#beforeitemmouseleave
             */
            'beforeitemmouseleave',
            /**
             * @event beforeitemclick
             * @inheritdoc Ext.view.View#beforeitemclick
             */
            'beforeitemclick',
            /**
             * @event beforeitemdblclick
             * @inheritdoc Ext.view.View#beforeitemdblclick
             */
            'beforeitemdblclick',
            /**
             * @event beforeitemcontextmenu
             * @inheritdoc Ext.view.View#beforeitemcontextmenu
             */
            'beforeitemcontextmenu',
            /**
             * @event itemmousedown
             * @inheritdoc Ext.view.View#itemmousedown
             */
            'itemmousedown',
            /**
             * @event itemmouseup
             * @inheritdoc Ext.view.View#itemmouseup
             */
            'itemmouseup',
            /**
             * @event itemmouseenter
             * @inheritdoc Ext.view.View#itemmouseenter
             */
            'itemmouseenter',
            /**
             * @event itemmouseleave
             * @inheritdoc Ext.view.View#itemmouseleave
             */
            'itemmouseleave',
            /**
             * @event itemclick
             * @inheritdoc Ext.view.View#itemclick
             */
            'itemclick',
            /**
             * @event itemdblclick
             * @inheritdoc Ext.view.View#itemdblclick
             */
            'itemdblclick',
            /**
             * @event itemcontextmenu
             * @inheritdoc Ext.view.View#itemcontextmenu
             */
            'itemcontextmenu',
            /**
             * @event beforecellclick
             * @inheritdoc Ext.view.Table#beforecellclick
             */
            'beforecellclick',
            /**
             * @event cellclick
             * @inheritdoc Ext.view.Table#cellclick
             */
            'cellclick',
            /**
             * @event beforecelldblclick
             * @inheritdoc Ext.view.Table#beforecelldblclick
             */
            'beforecelldblclick',
            /**
             * @event celldblclick
             * @inheritdoc Ext.view.Table#celldblclick
             */
            'celldblclick',
            /**
             * @event beforecellcontextmenu
             * @inheritdoc Ext.view.Table#beforecellcontextmenu
             */
            'beforecellcontextmenu',
            /**
             * @event cellcontextmenu
             * @inheritdoc Ext.view.Table#cellcontextmenu
             */
            'cellcontextmenu',
            /**
             * @event beforecellmousedown
             * @inheritdoc Ext.view.Table#beforecellmousedown
             */
            'beforecellmousedown',
            /**
             * @event cellmousedown
             * @inheritdoc Ext.view.Table#cellmousedown
             */
            'cellmousedown',
            /**
             * @event beforecellmouseup
             * @inheritdoc Ext.view.Table#beforecellmouseup
             */
            'beforecellmouseup',
            /**
             * @event cellmouseup
             * @inheritdoc Ext.view.Table#cellmouseup
             */
            'cellmouseup',
            /**
             * @event beforecellkeydown
             * @inheritdoc Ext.view.Table#beforecellkeydown
             */
            'beforecellkeydown',
            /**
             * @event cellkeydown
             * @inheritdoc Ext.view.Table#cellkeydown
             */
            'cellkeydown',
            /**
             * @event beforeitemkeydown
             * @inheritdoc Ext.view.Table#beforeitemkeydown
             */
            'beforeitemkeydown',
            /**
             * @event itemkeydown
             * @inheritdoc Ext.view.Table#itemkeydown
             */
            'itemkeydown',
            /**
             * @event beforecontainermousedown
             * @inheritdoc Ext.view.View#beforecontainermousedown
             */
            'beforecontainermousedown',
            /**
             * @event beforecontainermouseup
             * @inheritdoc Ext.view.View#beforecontainermouseup
             */
            'beforecontainermouseup',
            /**
             * @event beforecontainermousedown
             * @inheritdoc Ext.view.View#beforecontainermousedown
             */
            'beforecontainermousedown',
            /**
             * @event beforecontainermouseover
             * @inheritdoc Ext.view.View#beforecontainermouseover
             */
            'beforecontainermouseover',
            /**
             * @event beforecontainermouseout
             * @inheritdoc Ext.view.View#beforecontainermouseout
             */
            'beforecontainermouseout',
            /**
             * @event beforecontainerclick
             * @inheritdoc Ext.view.View#beforecontainerclick
             */
            'beforecontainerclick',
            /**
             * @event beforecontainerdblclick
             * @inheritdoc Ext.view.View#beforecontainerdblclick
             */
            'beforecontainerdblclick',
            /**
             * @event beforecontainercontextmenu
             * @inheritdoc Ext.view.View#beforecontainercontextmenu
             */
            'beforecontainercontextmenu',
            /**
             * @event beforecontainerkeydown
             * @inheritdoc Ext.view.View#beforecontainerkeydown
             */
            'beforecontainerkeydown',
            /**
             * @event containermouseup
             * @inheritdoc Ext.view.View#containermouseup
             */
            'containermouseup',
            /**
             * @event containermousedown
             * @inheritdoc Ext.view.View#containermousedown
             */
            'containermousedown',
            /**
             * @event containermouseover
             * @inheritdoc Ext.view.View#containermouseover
             */
            'containermouseover',
            /**
             * @event containermouseout
             * @inheritdoc Ext.view.View#containermouseout
             */
            'containermouseout',
            /**
             * @event containerclick
             * @inheritdoc Ext.view.View#containerclick
             */
            'containerclick',
            /**
             * @event containerdblclick
             * @inheritdoc Ext.view.View#containerdblclick
             */
            'containerdblclick',
            /**
             * @event containercontextmenu
             * @inheritdoc Ext.view.View#containercontextmenu
             */
            'containercontextmenu',
            /**
             * @event containerkeydown
             * @inheritdoc Ext.view.View#containerkeydown
             */
            'containerkeydown',
            /**
             * @event selectionchange
             * @inheritdoc Ext.selection.Model#selectionchange
             */
            'selectionchange',
            /**
             * @event beforeselect
             * @inheritdoc Ext.selection.RowModel#beforeselect
             */
            'beforeselect',
            /**
             * @event select
             * @inheritdoc Ext.selection.RowModel#select
             */
            'select',
            /**
             * @event beforedeselect
             * @inheritdoc Ext.selection.RowModel#beforedeselect
             */
            'beforedeselect',
            /**
             * @event deselect
             * @inheritdoc Ext.selection.RowModel#deselect
             */
            'deselect'
        ]);

        me.callParent(arguments);
        me.addStateEvents(['columnresize', 'columnmove', 'columnhide', 'columnshow', 'sortchange', 'filterchange', 'groupchange']);

        // If lockable, the headerCt is just a collection of Columns, not a Container
        if (!me.lockable && me.headerCt) {
            me.headerCt.on('afterlayout', me.onRestoreHorzScroll, me);
        }
    },

    // Private. Determine if there are any columns with a locked configuration option
    hasLockedColumns: function(columns) {
        var i,
            len,
            column;

        // In case they specified a config object with items...
        if (Ext.isObject(columns)) {
            columns = columns.items;
        }
        for (i = 0, len = columns.length; i < len; i++) {
            column = columns[i];
            if (!column.processed && column.locked) {
                return true;
            }
        }
    },

    relayHeaderCtEvents: function (headerCt) {
        this.relayEvents(headerCt, [
            /**
             * @event columnresize
             * @inheritdoc Ext.grid.header.Container#columnresize
             */
            'columnresize',
            /**
             * @event columnmove
             * @inheritdoc Ext.grid.header.Container#columnmove
             */
            'columnmove',
            /**
             * @event columnhide
             * @inheritdoc Ext.grid.header.Container#columnhide
             */
            'columnhide',
            /**
             * @event columnshow
             * @inheritdoc Ext.grid.header.Container#columnshow
             */
            'columnshow',
            /**
             * @event columnschanged
             * @inheritdoc Ext.grid.header.Container#columnschanged
             */
            'columnschanged',
            /**
             * @event sortchange
             * @inheritdoc Ext.grid.header.Container#sortchange
             */
            'sortchange',
            /**
             * @event headerclick
             * @inheritdoc Ext.grid.header.Container#headerclick
             */
            'headerclick',
            /**
             * @event headercontextmenu
             * @inheritdoc Ext.grid.header.Container#headercontextmenu
             */
            'headercontextmenu',
            /**
             * @event headertriggerclick
             * @inheritdoc Ext.grid.header.Container#headertriggerclick
             */
            'headertriggerclick'
        ]);
    },

    getState: function(){
        var me = this,
            state = me.callParent(),
            storeState = me.store.getState();

        state = me.addPropertyToState(state, 'columns', me.headerCt.getColumnsState());

        if (storeState) {
            state.storeState = storeState;
        }
        return state;
    },

    applyState: function(state) {
        var me = this,
            sorter = state.sort,
            storeState = state.storeState,
            store = me.store,
            columns = state.columns;

        delete state.columns;

        // Ensure superclass has applied *its* state.
        // AbstractComponent saves dimensions (and anchor/flex) plus collapsed state.
        me.callParent(arguments);

        if (columns) {
            me.headerCt.applyColumnsState(columns);
        }

        // Old stored sort state. Deprecated and will die out.
        if (sorter) {
            if (store.remoteSort) {
                // Pass false to prevent a sort from occurring
                store.sort({
                    property: sorter.property,
                    direction: sorter.direction,
                    root: sorter.root
                }, null, false);
            } else {
                store.sort(sorter.property, sorter.direction);
            }
        }
        // New storeState which encapsulates groupers, sorters and filters
        else if (storeState) {
            store.applyState(storeState);
        }
    },

    /**
     * Returns the store associated with this Panel.
     * @return {Ext.data.Store} The store
     */
    getStore: function(){
        return this.store;
    },

    /**
     * Gets the view for this panel.
     * @return {Ext.view.Table}
     */
    getView: function() {
        var me = this,
            sm;

        if (!me.view) {
            sm = me.getSelectionModel();

            // TableView injects the view reference into this grid so that we have a reference as early as possible
            Ext.widget(Ext.apply({

                // Features need a reference to the grid, so configure a reference into the View
                grid: me,
                deferInitialRefresh: me.deferRowRender !== false,
                trackOver: me.trackMouseOver !== false,
                scroll: me.scroll,
                xtype: me.viewType,
                store: me.store,
                headerCt: me.headerCt,
                columnLines: me.columnLines,
                rowLines: me.rowLines,
                selModel: sm,
                features: me.features,
                panel: me,
                emptyText: me.emptyText || ''
            }, me.viewConfig));

            // Normalize the application of the markup wrapping the emptyText config.
            // `emptyText` can now be defined on the grid as well as on its viewConfig, and this led to the emptyText not
            // having the wrapping markup when it was defined in the viewConfig. It should be backwards compatible.
            // Note that in the unlikely event that emptyText is defined on both the grid config and the viewConfig that the viewConfig wins.
            if (me.view.emptyText) {
                me.view.emptyText = '<div class="' + me.emptyCls + '">' + me.view.emptyText + '</div>';
            }

            // TableView's custom component layout, Ext.view.TableLayout requires a reference to the headerCt because it depends on the headerCt doing its work.
            me.view.getComponentLayout().headerCt = me.headerCt;

            me.mon(me.view, {
                uievent: me.processEvent,
                scope: me
            });
            sm.view = me.view;
            me.headerCt.view = me.view;
        }
        return me.view;
    },
    
    getColumnManager: function(){
        return this.columnManager;
    },
    
    getVisibleColumnManager: function(){
        return this.visibleColumnManager;
    },
    
    getTopLevelColumnManager: function(){
        var ownerLock = this.ownerLockable;
        return ownerLock ? ownerLock.getColumnManager() : this.getColumnManager();    
    },
    
    getTopLevelVisibleColumnManager: function(){
        var ownerLock = this.ownerLockable;
        return ownerLock ? ownerLock.getVisibleColumnManager() : this.getVisibleColumnManager();    
    },

    /**
     * @private
     * autoScroll is never valid for all classes which extend TablePanel.
     */
    setAutoScroll: Ext.emptyFn,

    /**
     * @private
     * Processes UI events from the view. Propagates them to whatever internal Components need to process them.
     * @param {String} type Event type, eg 'click'
     * @param {Ext.view.Table} view TableView Component
     * @param {HTMLElement} cell Cell HtmlElement the event took place within
     * @param {Number} recordIndex Index of the associated Store Model (-1 if none)
     * @param {Number} cellIndex Cell index within the row
     * @param {Ext.EventObject} e Original event
     */
    processEvent: function(type, view, cell, recordIndex, cellIndex, e, record, row) {
        var me = this,
            header;

        if (cellIndex !== -1) {
            header = me.getColumnManager().getHeaderAtIndex(cellIndex);
            return header.processEvent.apply(header, arguments);
        }
    },

    /**
     * This method is obsolete in 4.1. The closest equivalent in
     * 4.1 is {@link #doLayout}, but it is also possible that no
     * layout is needed.
     * @deprecated 4.1
     */
    determineScrollbars: function () {
    },

    /**
     * This method is obsolete in 4.1. The closest equivalent in 4.1 is
     * {@link Ext.AbstractComponent#updateLayout}, but it is also possible that no layout
     * is needed.
     * @deprecated 4.1
     */
    invalidateScroller: function () {
    },

    scrollByDeltaY: function(yDelta, animate) {
        this.getView().scrollBy(0, yDelta, animate);
    },

    scrollByDeltaX: function(xDelta, animate) {
        this.getView().scrollBy(xDelta, 0, animate);
    },

    afterCollapse: function() {
        this.saveScrollPos();
        this.callParent(arguments);
    },

    afterExpand: function() {
        this.callParent(arguments);
        this.restoreScrollPos();
    },

    saveScrollPos: Ext.emptyFn,

    restoreScrollPos: Ext.emptyFn,
    
    onHeaderResize: function(){
        this.delayScroll();
    },

    // Update the view when a header moves
    onHeaderMove: function(headerCt, header, colsToMove, fromIdx, toIdx) {
        var me = this;

        // If there are Features or Plugins which create DOM which must match column order, they set the optimizedColumnMove flag to false.
        // In this case we must refresh the view on column move.
        if (me.optimizedColumnMove === false) {
            me.view.refresh();
        }

        // Simplest case for default DOM structure is just to swap the columns round in the view.
        else {
            me.view.moveColumn(fromIdx, toIdx, colsToMove);
        }
        me.delayScroll();
    },

    // Section onHeaderHide is invoked after view.
    onHeaderHide: function(headerCt, header) {
        this.view.refresh();
        this.delayScroll();
    },

    onHeaderShow: function(headerCt, header) {
        this.view.refresh();
        this.delayScroll();
    },
    
    // To be triggered on add/remove/move for a leaf header
    onHeadersChanged: function(headerCt, header) {
        var me = this;
        if (me.rendered && !me.reconfiguring) {
            me.view.refresh();
            me.delayScroll();
        }
    },

    delayScroll: function(){
        var target = this.getScrollTarget().el;
        if (target) {
            this.scrollTask.delay(10, null, null, [target.dom.scrollLeft]);
        }
    },

    /**
     * @private
     * Fires the TablePanel's viewready event when the view declares that its internal DOM is ready
     */
    onViewReady: function() {
         this.fireEvent('viewready', this);   
    },

    /**
     * @private
     * Tracks when things happen to the view and preserves the horizontal scroll position.
     */
    onRestoreHorzScroll: function() {
        var left = this.scrollLeftPos;
        if (left) {
            // We need to restore the body scroll position here
            this.syncHorizontalScroll(left, true);
        }
    },

    getScrollerOwner: function() {
        var rootCmp = this;
        if (!this.scrollerOwner) {
            rootCmp = this.up('[scrollerOwner]');
        }
        return rootCmp;
    },

    /**
     * Gets left hand side marker for header resizing.
     * @private
     */
    getLhsMarker: function() {
        var me = this;
        return me.lhsMarker || (me.lhsMarker = Ext.DomHelper.append(me.el, {
            role: 'presentation',
            cls: me.resizeMarkerCls
        }, true));
    },

    /**
     * Gets right hand side marker for header resizing.
     * @private
     */
    getRhsMarker: function() {
        var me = this;

        return me.rhsMarker || (me.rhsMarker = Ext.DomHelper.append(me.el, {
            role: 'presentation',
            cls: me.resizeMarkerCls
        }, true));
    },

    /**
     * Returns the selection model being used and creates it via the configuration if it has not been created already.
     * @return {Ext.selection.Model} selModel
     */
    getSelectionModel: function(){
        var me = this,
            selModel = me.selModel,
            applyMode, mode, type;
        
        if (!selModel) {
            selModel = {};
            // no config, set our own mode
            applyMode = true;
        }

        if (!selModel.events) {
            // only config provided, set our mode if one doesn't exist on the config
            type = selModel.selType || me.selType;
            applyMode = !selModel.mode;
            selModel = me.selModel = Ext.create('selection.' + type, selModel);
        }

        if (me.simpleSelect) {
            mode = 'SIMPLE';
        } else if (me.multiSelect) {
            mode = 'MULTI';
        }

        Ext.applyIf(selModel, {
            allowDeselect: me.allowDeselect
        });
        
        if (mode && applyMode) {
            selModel.setSelectionMode(mode);
        }

        if (!selModel.hasRelaySetup) {
            me.relayEvents(selModel, [
                'selectionchange', 'beforeselect', 'beforedeselect', 'select', 'deselect'
            ]);
            selModel.hasRelaySetup = true;
        }

        // lock the selection model if user
        // has disabled selection
        if (me.disableSelection) {
            selModel.locked = true;
        }
        return selModel;
    },
    
    getScrollTarget: function(){
        var owner = this.getScrollerOwner(),
            items = owner.query('tableview');
            
        return items[1] || items[0];
    },

    onHorizontalScroll: function(event, target) {
        this.syncHorizontalScroll(target.scrollLeft);
    },
    
    syncHorizontalScroll: function(left, setBody) {
        var me = this,
            scrollTarget;
            
        setBody = setBody === true;
        // Only set the horizontal scroll if we've changed position,
        // so that we don't set this on vertical scrolls
        if (me.rendered && (setBody || left !== me.scrollLeftPos)) {
            // Only set the body position if we're reacting to a refresh, otherwise
            // we just need to set the header.
            if (setBody) {   
                scrollTarget = me.getScrollTarget();
                scrollTarget.el.dom.scrollLeft = left;
            }
            me.headerCt.el.dom.scrollLeft = left;
            me.scrollLeftPos = left;
        }
    },

    // template method meant to be overriden
    onStoreLoad: Ext.emptyFn,

    getEditorParent: function() {
        return this.body;
    },

    bindStore: function(store, initial) {
        var me = this,
            view = me.getView(),
            bufferedStore = store && store.buffered,
            bufferedRenderer;

        // Bind to store immediately because subsequent processing looks for grid's store property
        me.store = store;

        // If the Store is buffered, create a BufferedRenderer to monitor the View's scroll progress
        // and scroll rows on/off when it detects we are nearing an edge.
        // MUST be done before store is bound to the view so that the BufferedRenderer may inject its getViewRange implementation
        // before the view tries to refresh.
        bufferedRenderer = me.findPlugin('bufferedrenderer');
        if (bufferedRenderer) {
            me.verticalScroller = bufferedRenderer;
            // If we're in a reconfigure rebind the BufferedRenderer
            if (bufferedRenderer.store) {
                bufferedRenderer.bindStore(store);
            }
        } else if (bufferedStore) {
            me.verticalScroller = bufferedRenderer = me.addPlugin(Ext.apply({
                ptype: 'bufferedrenderer'
            }, me.initialConfig.verticalScroller));
        }

        if (view.store !== store) {
            // If coming from a reconfigure, we need to set the actual store property on the view. Setting the
            // store will then also set the dataSource.
            // 
            // Note that if it's a grid feature then this is sorted out in view.bindStore(), and it's own
            // implementation of .bindStore() will be called.
            view.bindStore(store, false);
        }

        me.mon(store, {
            load: me.onStoreLoad,
            scope: me
        });
        me.storeRelayers = me.relayEvents(store, [
            /**
             * @event filterchange
             * @inheritdoc Ext.data.Store#filterchange
             */
            'filterchange',
            /**
             * @event groupchange
             * @inheritdoc Ext.data.Store#groupchange
             */
            'groupchange'
        ]);

        // If buffered rendering is being used, scroll position must be preserved across refreshes
        if (bufferedRenderer) {
            me.invalidateScrollerOnRefresh = false;
        }

        if (me.invalidateScrollerOnRefresh !== undefined) {
            view.preserveScrollOnRefresh = !me.invalidateScrollerOnRefresh;
        }
    },

    unbindStore: function() {
        var me = this,
            store = me.store;

        if (store) {
            me.store = null;
            me.mun(store, {
                load: me.onStoreLoad,
                scope: me
            });
            Ext.destroy(me.storeRelayers);
        }
    },

    // documented on GridPanel
    reconfigure: function(store, columns) {
        var me = this,
            view = me.getView(),
            originalDeferinitialRefresh,
            oldStore = me.store,
            headerCt = me.headerCt,
            oldColumns = headerCt ? headerCt.items.getRange() : me.columns;

        // Make copy in case the beforereconfigure listener mutates it.
        if (columns) {
            columns = Ext.Array.slice(columns);
        }

        me.reconfiguring = true;
        me.fireEvent('beforereconfigure', me, store, columns, oldStore, oldColumns);
        if (me.lockable) {
            me.reconfigureLockable(store, columns);
        } else {
            Ext.suspendLayouts();
            if (columns) {
                // new columns, delete scroll pos
                delete me.scrollLeftPos;
                headerCt.removeAll();
                headerCt.add(columns);
            }
            // The following test compares the result of an assignment of the store var with the oldStore var
            // This saves a large amount of code.
            if (store && (store = Ext.StoreManager.lookup(store)) !== oldStore) {
                // Only unbind the store if a new one was passed
                if (me.store) {
                    me.unbindStore();
                }

                // On reconfigure, view refresh must be inline.
                originalDeferinitialRefresh = view.deferInitialRefresh;
                view.deferInitialRefresh = false;
                me.bindStore(store);
                view.deferInitialRefresh = originalDeferinitialRefresh;
            } else {
                me.getView().refresh();
            }
            headerCt.setSortState();
            Ext.resumeLayouts(true);
        }
        me.fireEvent('reconfigure', me, store, columns, oldStore, oldColumns);
        delete me.reconfiguring;
    },

    beforeDestroy: function(){
        var task = this.scrollTask;
        if (task) {
            task.cancel();
            this.scrollTask = null;
        }
        this.callParent();
    },

    onDestroy: function(){
        var me = this;
        if (me.lockable) {
            me.destroyLockable();
        }
        me.callParent();
        me.columns = me.storeRelayers = me.columnManager = me.visibleColumnManager = null;
    },
    
    destroy: function() {
        // Clear out references here because other things (plugins/features) may need to know about them during destruction
        var me = this;
        me.callParent();
        if (me.isDestroyed) {
            me.view = me.selModel = me.headerCt = null;
        }
    }
});

/**
 * Utility class for manipulating CSS rules
 * @singleton
 */
Ext.define('Ext.util.CSS', function() {
    var CSS,
        rules = null,
        doc = document,
        camelRe = /(-[a-z])/gi,
        camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };

    return {

        singleton: true,

        rules: rules,

        initialized: false,

        constructor: function() {
            // Cache a reference to the singleton
            CSS = this;
        },

        /**
         * Creates a stylesheet from a text blob of rules.
         * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document.
         * @param {String} cssText The text containing the css rules
         * @param {String} id An id to add to the stylesheet for later removal
         * @return {CSSStyleSheet}
         */
        createStyleSheet : function(cssText, id) {
            var ss,
                head = doc.getElementsByTagName("head")[0],
                styleEl = doc.createElement("style");

            styleEl.setAttribute("type", "text/css");
            if (id) {
               styleEl.setAttribute("id", id);
            }

            if (Ext.isIE) {
               head.appendChild(styleEl);
               ss = styleEl.styleSheet;
               ss.cssText = cssText;
            } else {
                try{
                    styleEl.appendChild(doc.createTextNode(cssText));
                } catch(e) {
                   styleEl.cssText = cssText;
                }
                head.appendChild(styleEl);
                ss = styleEl.styleSheet ? styleEl.styleSheet : (styleEl.sheet || doc.styleSheets[doc.styleSheets.length-1]);
            }
            CSS.cacheStyleSheet(ss);
            return ss;
        },

        /**
         * Removes a style or link tag by id
         * @param {String} id The id of the tag
         */
        removeStyleSheet : function(id) {
            var existing = doc.getElementById(id);
            if (existing) {
                existing.parentNode.removeChild(existing);
            }
        },

        /**
         * Dynamically swaps an existing stylesheet reference for a new one
         * @param {String} id The id of an existing link tag to remove
         * @param {String} url The href of the new stylesheet to include
         */
        swapStyleSheet : function(id, url) {
            var ss;
            CSS.removeStyleSheet(id);
            ss = doc.createElement("link");
            ss.setAttribute("rel", "stylesheet");
            ss.setAttribute("type", "text/css");
            ss.setAttribute("id", id);
            ss.setAttribute("href", url);
            doc.getElementsByTagName("head")[0].appendChild(ss);
        },

        /**
         * Refresh the rule cache if you have dynamically added stylesheets
         * @return {Object} An object (hash) of rules indexed by selector
         */
        refreshCache : function() {
            return CSS.getRules(true);
        },

        // @private
        cacheStyleSheet : function(ss) {
            if (!rules) {
                rules = CSS.rules = {};
            }
            try {// try catch for cross domain access issue
                var ssRules = ss.cssRules || ss.rules,
                    i = ssRules.length - 1,
                    imports = ss.imports,
                    len = imports ? imports.length : 0,
                    rule, j;
                    
                // Old IE has a different way of handling imports
                for (j = 0; j < len; ++j) {
                    CSS.cacheStyleSheet(imports[j]);
                }

                for (; i >= 0; --i) {
                    rule = ssRules[i];
                    // If it's an @import rule, import its stylesheet
                    if (rule.styleSheet) {
                        CSS.cacheStyleSheet(rule.styleSheet);
                    }
                    CSS.cacheRule(rule, ss);
                }
            } catch(e) {}
        },

        cacheRule: function(cssRule, styleSheet) {
            // If it's an @import rule, import its stylesheet
            if (cssRule.styleSheet) {
                return CSS.cacheStyleSheet(cssRule.styleSheet);
            }

            var selectorText = cssRule.selectorText,
                selectorCount, j;

            if (selectorText) {

                // Split in case there are multiple, comma-delimited selectors
                selectorText = selectorText.split(',');
                selectorCount = selectorText.length;
                for (j = 0; j < selectorCount; j++) {
                    // IE<8 does not keep a reference to parentStyleSheet in the rule, so we
                    // must cache an object like this until IE<8 is deprecated.
                    rules[Ext.String.trim(selectorText[j]).toLowerCase()] = {
                        parentStyleSheet: styleSheet,
                        cssRule: cssRule
                    };
                };
            }
        },

        /**
         * Gets all css rules for the document
         * @param {Boolean} refreshCache true to refresh the internal cache
         * @return {Object} An object (hash) of rules indexed by selector
         */
        getRules : function(refreshCache) {
            var result = {},
                selector;

            if (rules === null || refreshCache) {
                CSS.refreshCache();
            }
            for (selector in rules) {
                result[selector] = rules[selector].cssRule;
            }
            return result;
        },
        
        refreshCache: function() {
            var ds = doc.styleSheets,
                i = 0,
                len = ds.length;

            rules = CSS.rules = {}
            for (; i < len; i++) {
                try {
                    if (!ds[i].disabled) {
                        CSS.cacheStyleSheet(ds[i]);
                    }
                } catch(e) {}
            }
        },

        /**
         * Gets an an individual CSS rule by selector(s)
         * @param {String/String[]} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
         * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
         * @return {CSSStyleRule} The CSS rule or null if one is not found
         */
        getRule: function(selector, refreshCache, rawCache) {
            var i, result;

            if (!rules || refreshCache) {
                CSS.refreshCache();
            }
            if (!Ext.isArray(selector)) {
                result = rules[selector.toLowerCase()]
                if (result && !rawCache) {
                    result = result.cssRule;
                }
                return result || null;
            }
            for (i = 0; i < selector.length; i++) {
                if (rules[selector[i]]) {
                    return rawCache ? rules[selector[i].toLowerCase()] : rules[selector[i].toLowerCase()].cssRule;
                }
            }
            return null;
        },

        /**
         * Creates a rule.
         * @param {CSSStyleSheet} styleSheet The StyleSheet to create the rule in as returned from {@link #createStyleSheet}.
         * @param {String} selector The selector to target the rule.
         * @param {String} property The cssText specification eg `"color:red;font-weight:bold;text-decoration:underline"`
         * @return {CSSStyleRule} The created rule
         */
        createRule: function(styleSheet, selector, cssText) {
            var result,
                ruleSet = styleSheet.cssRules || styleSheet.rules,
                index = ruleSet.length;

            if (styleSheet.insertRule) {
                styleSheet.insertRule(selector + '{' + cssText + '}', index);
            } else {
                styleSheet.addRule(selector, cssText||' ');
            }
            CSS.cacheRule(result = ruleSet[index], styleSheet);
            return result;
        },

        /**
         * Updates a rule property
         * @param {String/String[]} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
         * @param {String} property The css property or a cssText specification eg `"color:red;font-weight:bold;text-decoration:underline"`
         * @param {String} value The new value for the property
         * @return {Boolean} true If a rule was found and updated
         */
        updateRule : function(selector, property, value) {
            var rule, i, styles;
            if (!Ext.isArray(selector)) {
                rule = CSS.getRule(selector);
                if (rule) {
                    // 2 arg form means cssText sent, so parse it and update each style
                    if (arguments.length == 2) {
                        styles = Ext.Element.parseStyles(property);
                        for (property in styles) {
                            rule.style[property.replace(camelRe, camelFn)] = styles[property];
                        }
                    } else {
                        rule.style[property.replace(camelRe, camelFn)] = value;
                    }
                    return true;
                }
            } else {
                for (i = 0; i < selector.length; i++) {
                    if (CSS.updateRule(selector[i], property, value)) {
                        return true;
                    }
                }
            }
            return false;
        },

        deleteRule: function(selector) {
            var rule = CSS.getRule(selector, false, true),
                styleSheet, index;

            if (rule) {
                styleSheet = rule.parentStyleSheet;
                index = Ext.Array.indexOf(styleSheet.cssRules || styleSheet.rules, rule.cssRule);
                if (styleSheet.deleteRule) {
                    styleSheet.deleteRule(index);
                } else {
                    styleSheet.removeRule(index);
                }
                delete rules[selector];
            }
        }
    };
});

/**
 *  Component layout for {@link Ext.view.Table}
 *  @private
 * 
 */
Ext.define('Ext.view.TableLayout', {
    extend:  Ext.layout.component.Auto ,
                               

    alias: ['layout.tableview'],
    type: 'tableview',

    beginLayout: function(ownerContext) {
        var me = this,
            otherSide = me.owner.lockingPartner,
            owner = me.owner;

        me.callParent(arguments);
        
        // If we are in a twinned grid (locked view) then set up bidirectional links with the other side's layout context
        if (otherSide) {
            me.lockedGrid = me.owner.up('[lockable]');
            me.lockedGrid.needsRowHeightSync = true;
            if (!ownerContext.lockingPartner) {
                ownerContext.lockingPartner = ownerContext.context.getItem(otherSide, otherSide.el);
                if (ownerContext.lockingPartner && !ownerContext.lockingPartner.lockingPartner) {
                    ownerContext.lockingPartner.lockingPartner = ownerContext;
                }
            }
        }

        // Grab a ContextItem for the header container
        ownerContext.headerContext = ownerContext.context.getCmp(me.headerCt);

        // Grab ContextItem for the table only if there is a table to size
        if (me.owner.body.dom) {
            ownerContext.bodyContext = ownerContext.getEl(me.owner.body);
        }
        if (Ext.isWebKit) {
            owner.el.select(owner.getBodySelector()).setStyle('table-layout', 'auto');
        }
    },

    calculate: function(ownerContext) {
        var me = this,
            lockingPartner = me.lockingPartner,
            owner = me.owner,
            contentHeight = 0,
            emptyEl;

        // We can only complete our work (setting the CSS rules governing column widths) if the
        // Grid's HeaderContainer's ColumnLayout has set the widths of its columns.
        if (ownerContext.headerContext.hasProp('columnWidthsDone')) {
            if (!me.setColumnWidths(ownerContext)) {
                me.done = false;
                return;
            }
            ownerContext.state.columnWidthsSynced = true;
            if (ownerContext.bodyContext) {
                emptyEl = me.owner.el.down('.' + owner.ownerCt.emptyCls, true);
                if (!emptyEl) {
                    contentHeight = ownerContext.bodyContext.el.dom.offsetHeight;
                    ownerContext.bodyContext.setHeight(contentHeight, false);
                } else {
                    contentHeight = emptyEl.offsetHeight;
                }
                // If there is horizontal overflow, and the grid is shrinkwrapping height, then allow the horizontal scrollbar to contibute to contentHeight
                if (ownerContext.headerContext.state.boxPlan.tooNarrow && ownerContext.ownerCtContext.sizeModel.height.shrinkWrap) {
                    contentHeight += Ext.getScrollbarSize().height;
                }
                ownerContext.setProp('contentHeight', contentHeight);
            }

            // If we are part of a twinned table view set (locking grid)
            // Then only complete when both sides are complete.
            if (lockingPartner && !lockingPartner.state.columnWidthsSynced) {
                me.done = false;
            } else {
                me.callParent(arguments);
            }

        } else {
            me.done = false;
        }
    },

    measureContentHeight: function(ownerContext) {
        var lockingPartner = ownerContext.lockingPartner;

        // Only able to produce a valid contentHeight if there's no table
        // ... or we have flushed all column widths to the table (or both tables if we are a pair)
        if (!ownerContext.bodyContext || (ownerContext.state.columnWidthsSynced && (!lockingPartner || lockingPartner.state.columnWidthsSynced))) {
            return this.callParent(arguments);
        }
    },

    setColumnWidths: function(ownerContext) {
        var me = this,
            owner = me.owner,
            context = ownerContext.context,
            columns = me.headerCt.getVisibleGridColumns(),
            column,
            i = 0, len = columns.length,
            tableWidth = 0,
            columnLineWidth = 0,
            childContext,
            colWidth,
            isContentBox = !Ext.isBorderBox;

        // So that the setProp can trigger this layout.
        if (context) {
            context.currentLayout = me;
        }

        // Set column width corresponding to each header
        for (i = 0; i < len; i++) {
            column = columns[i];
            childContext = context.getCmp(column);
            colWidth = childContext.props.width;
            if (isNaN(colWidth)) {
                // We don't have a width set, so we need to trigger when this child
                // actually gets a width assigned so we can continue. Technically this
                // shouldn't happen however we have a bug inside ColumnLayout where
                // columnWidthsDone is set incorrectly. This is just a workaround.
                childContext.getProp('width');
                return false;
            }
            tableWidth += colWidth;
            // https://sencha.jira.com/browse/EXTJSIV-9263 - Browsers which cannot be switched to border box when doctype present (IE6 & IE7) - must subtract borders width from width of cells.
            if (isContentBox && owner.columnLines) {
                // https://sencha.jira.com/browse/EXTJSIV-9744 - default border width to 1 because
                // We are looking at the *header* border widths and Neptune, being a borderless theme
                // omits the border from the last column *HEADER*. But we are interrogating that to
                // know the width of border lines between cells which are not omitted.
                if (!columnLineWidth) {
                    columnLineWidth = context.getCmp(column).borderInfo.width || 1;
                }
                colWidth -= columnLineWidth;
            }

            // Select column sizing <col> elements within every <table> within the grid.
            // 90% of the time, there will be only one table.
            // The RowWrap and Summary Features embed tables within colspanning cells, and these also
            // get <colgroup><col></colgroup> sizers which need updating.
            // On IE8, sizing <col> elements to control column width was about 2.25 times
            // faster than selecting all the cells in the column to be resized.
            // Column sizing using dynamic CSS rules is *extremely* expensive on IE.
            owner.body.select(owner.getColumnSizerSelector(column)).setWidth(colWidth);

        }
        // Set widths of all tables (includes tables embedded in RowWrap and Summary rows)
        owner.el.select(owner.getBodySelector()).setWidth(tableWidth);
        return true;
    },

    finishedLayout: function() {
        var me = this,
            owner = me.owner;

        me.callParent(arguments);

        if (Ext.isWebKit) {
            owner.el.select(owner.getBodySelector()).setStyle('table-layout', '');
        }
        // Make sure only one side gets to do the sync on completion - it's an expensive process.
        // Only do it if the syncRowHeightConfig hasn't been set to false.
        if (owner.refreshCounter && me.lockedGrid && me.lockedGrid.syncRowHeight && me.lockedGrid.needsRowHeightSync) {
            me.lockedGrid.syncRowHeights();
            me.lockedGrid.needsRowHeightSync = false;
        }
    }
});

/**
 * @private
 * A cache of View elements keyed using the index of the associated record in the store.
 * 
 * This implements the methods of {Ext.dom.CompositeElement} which are used by {@link Ext.view.AbstractView}
 * to privide a map of record nodes and methods to manipulate the nodes.
 */
Ext.define('Ext.view.NodeCache', {
    constructor: function(view) {
        this.view = view;
        this.clear();
        this.el = new Ext.dom.AbstractElement.Fly();
    },

    /**
    * Removes all elements from this NodeCache.
    * @param {Boolean} [removeDom] True to also remove the elements from the document.
    */
    clear: function(removeDom) {
        var me = this,
            elements = this.elements,
            i, el;

        if (removeDom) {
            for (i in elements) {
                el = elements[i];
                el.parentNode.removeChild(el);
            }
        }
        me.elements = {};
        me.count = me.startIndex = 0;
        me.endIndex = -1;
    },

    /**
    * Clears this NodeCache and adds the elements passed.
    * @param {HTMLElement[]} els An array of DOM elements from which to fill this NodeCache.
    * @return {Ext.view.NodeCache} this
    */
    fill: function(newElements, startIndex) {
        var me = this,
            elements = me.elements = {},
            i,
            len = newElements.length;

        if (!startIndex) {
            startIndex = 0;
        }
        for (i = 0; i < len; i++) {
            elements[startIndex + i] = newElements[i];
        }
        me.startIndex = startIndex;
        me.endIndex = startIndex + len - 1;
        me.count = len;
        return this;
    },

    insert: function(insertPoint, nodes) {
        var me = this,
            elements = me.elements,
            i,
            nodeCount = nodes.length;

        // If not inserting into empty cache, validate, and possibly shuffle.
        if (me.count) {

            // Move following nodes forwards by <nodeCount> positions
            if (insertPoint < me.count) {
                for (i = me.endIndex + nodeCount; i >= insertPoint + nodeCount; i--) {
                    elements[i] = elements[i - nodeCount];
                    elements[i].setAttribute('data-recordIndex', i);
                }
            }
            me.endIndex = me.endIndex + nodeCount;
        }
        // Empty cache. set up counters
        else {
            me.startIndex = insertPoint;
            me.endIndex = insertPoint + nodeCount - 1;
        }

        // Insert new nodes into place
        for (i = 0; i < nodeCount; i++, insertPoint++) {
            elements[insertPoint] = nodes[i];
            elements[insertPoint].setAttribute('data-recordIndex', insertPoint);
        }
        me.count += nodeCount;
    },

    item: function(index, asDom) {
        var el = this.elements[index],
            result = null;

        if (el) {
            result = asDom ? this.elements[index] : this.el.attach(this.elements[index]);
        }
        return result;
    },

    first: function(asDom) {
        return this.item(this.startIndex, asDom);
    },

    last: function(asDom) {
        return this.item(this.endIndex, asDom);
    },

    getCount : function() {
        return this.count;
    },

    slice: function(start, end) {
        var elements = this.elements,
            result = [],
            i;

        if (arguments.length < 2) {
            end = this.endIndex;
        } else {
            end = Math.min(this.endIndex, end - 1);
        }
        for (i = start||this.startIndex; i <= end; i++) {
            result.push(elements[i]);
        }
        return result;
    },

    /**
    * Replaces the specified element with the passed element.
    * @param {String/HTMLElement/Ext.Element/Number} el The id of an element, the Element itself, the index of the
    * element in this composite to replace.
    * @param {String/Ext.Element} replacement The id of an element or the Element itself.
    * @param {Boolean} [domReplace] True to remove and replace the element in the document too.
    */
    replaceElement: function(el, replacement, domReplace) {
        var elements = this.elements,
            index = (typeof el === 'number') ? el : this.indexOf(el);

        if (index > -1) {
            replacement = Ext.getDom(replacement);
            if (domReplace) {
                el = elements[index];
                el.parentNode.insertBefore(replacement, el);
                Ext.removeNode(el);
                replacement.setAttribute('data-recordIndex', index);
            }
            this.elements[index] = replacement;
        }
        return this;
    },

    /**
    * Find the index of the passed element within the composite collection.
    * @param {String/HTMLElement/Ext.Element/Number} el The id of an element, or an Ext.dom.Element, or an HtmlElement
    * to find within the composite collection.
    * @return {Number} The index of the passed Ext.dom.Element in the composite collection, or -1 if not found.
    */
    indexOf: function(el) {
        var elements = this.elements,
            index;

        el = Ext.getDom(el);
        for (index = this.startIndex; index <= this.endIndex; index++) {
            if (elements[index] === el) {
                return index;
            }
        }
        return -1;
    },

    removeRange: function(start, end, removeDom) {
        var me = this,
            elements = me.elements,
            el,
            i, removeCount, fromPos;

        if (end === undefined) {
            end = me.count;
        } else {
            end = Math.min(me.endIndex + 1, end + 1);
        }
        if (!start) {
            start = 0;
        }
        removeCount = end - start;
        for (i = start, fromPos = end; i < me.endIndex; i++, fromPos++) {
            // Within removal range and we are removing from DOM
            if (removeDom && i < end) {
                Ext.removeNode(elements[i]);
            }
            // If the from position is occupied, shuffle that entry back into reference "i"
            if (fromPos <= me.endIndex) {
                el = elements[i] = elements[fromPos];
                el.setAttribute('data-recordIndex', i);
            }
            // The from position has walked off the end, so delete reference "i"
            else {
                delete elements[i];
            }
        }
        me.count -= removeCount;
        me.endIndex -= removeCount;
    },

    /**
    * Removes the specified element(s).
    * @param {String/HTMLElement/Ext.Element/Number} el The id of an element, the Element itself, the index of the
    * element in this composite or an array of any of those.
    * @param {Boolean} [removeDom] True to also remove the element from the document
    */
    removeElement: function(keys, removeDom) {
        var me = this,
            inKeys,
            key,
            elements = me.elements,
            el,
            deleteCount,
            keyIndex = 0, index,
            fromIndex;

        // Sort the keys into ascending order so that we can iterate through the elements
        // collection, and delete items encountered in the keys array as we encounter them.
        if (Ext.isArray(keys)) {
            inKeys = keys;
            keys = [];
            deleteCount = inKeys.length;
            for (keyIndex = 0; keyIndex < deleteCount; keyIndex++) {
                key = inKeys[keyIndex];
                if (typeof key !== 'number') {
                    key = me.indexOf(key);
                }
                // Could be asked to remove data above the start, or below the end of rendered zone in a buffer rendered view
                // So only collect keys which are within our range
                if (key >= me.startIndex && key <= me.endIndex) {
                    keys[keys.length] = key;
                }
            }
            Ext.Array.sort(keys);
            deleteCount = keys.length;
        } else {
            // Could be asked to remove data above the start, or below the end of rendered zone in a buffer rendered view
            if (keys < me.startIndex || keys > me.endIndex) {
                return;
            }
            deleteCount = 1;
            keys = [keys];
        }

        // Iterate through elements starting at the element referenced by the first deletion key.
        // We also start off and index zero in the keys to delete array.
        for (index = fromIndex = keys[0], keyIndex = 0; index <= me.endIndex; index++, fromIndex++) {

            // If the current index matches the next key in the delete keys array, this 
            // entry is being deleted, so increment the fromIndex to skip it.
            // Advance to next entry in keys array.
            if (keyIndex < deleteCount && index === keys[keyIndex]) {
                fromIndex++;
                keyIndex++;
                if (removeDom) {
                    Ext.removeNode(elements[index]);
                }
            }

            // Shuffle entries forward of the delete range back into contiguity.
            if (fromIndex <= me.endIndex && fromIndex >= me.startIndex) {
                el = elements[index] = elements[fromIndex];
                el.setAttribute('data-recordIndex', index);
            } else {
                delete elements[index];
            }
        }
        me.endIndex -= deleteCount;
        me.count -= deleteCount;
    },

    /**
     * Appends/prepends records depending on direction flag
     * @param {Ext.data.Model[]} newRecords Items to append/prepend
     * @param {Number} direction `-1' = scroll up, `0` = scroll down.
     * @param {Number} removeCount The number of records to remove from the end. if scrolling
     * down, rows are removed from the top and the new rows are added at the bottom.
     */
    scroll: function(newRecords, direction, removeCount) {
        var me = this,
            elements = me.elements,
            recCount = newRecords.length,
            i, el, removeEnd,
            newNodes,
            nodeContainer = me.view.getNodeContainer(),
            frag = document.createDocumentFragment();

        // Scrolling up (content moved down - new content needed at top, remove from bottom)
        if (direction == -1) {
            for (i = (me.endIndex - removeCount) + 1; i <= me.endIndex; i++) {
                el = elements[i];
                delete elements[i];
                el.parentNode.removeChild(el);
            }
            me.endIndex -= removeCount;

            // grab all nodes rendered, not just the data rows
            newNodes = me.view.bufferRender(newRecords, me.startIndex -= recCount);
            for (i = 0; i < recCount; i++) {
                elements[me.startIndex + i] = newNodes[i];
                frag.appendChild(newNodes[i]);
            }
            nodeContainer.insertBefore(frag, nodeContainer.firstChild);
        }

        // Scrolling down (content moved up - new content needed at bottom, remove from top)
        else {
            removeEnd = me.startIndex + removeCount;
            for (i = me.startIndex; i < removeEnd; i++) {
                el = elements[i];
                delete elements[i];
                el.parentNode.removeChild(el);
            }
            me.startIndex = i;

            // grab all nodes rendered, not just the data rows
            newNodes = me.view.bufferRender(newRecords, me.endIndex + 1);
            for (i = 0; i < recCount; i++) {
                elements[me.endIndex += 1] = newNodes[i];
                frag.appendChild(newNodes[i]);
            }
            nodeContainer.appendChild(frag);
        }
        // Keep count consistent.
        me.count = me.endIndex - me.startIndex + 1;
    }
});

/**
 * This class encapsulates the user interface for a tabular data set.
 * It acts as a centralized manager for controlling the various interface
 * elements of the view. This includes handling events, such as row and cell
 * level based DOM events. It also reacts to events from the underlying {@link Ext.selection.Model}
 * to provide visual feedback to the user.
 *
 * This class does not provide ways to manipulate the underlying data of the configured
 * {@link Ext.data.Store}.
 *
 * This is the base class for both {@link Ext.grid.View} and {@link Ext.tree.View} and is not
 * to be used directly.
 */
Ext.define('Ext.view.Table', {
    extend:  Ext.view.View ,
    alias: 'widget.tableview',
               
                               
                               
                             
                               
                                  
      

    componentLayout: 'tableview',

    baseCls: Ext.baseCSSPrefix + 'grid-view',

    unselectableCls: Ext.baseCSSPrefix + 'unselectable',

    /**
     * @cfg {String} [firstCls='x-grid-cell-first']
     * A CSS class to add to the *first* cell in every row to enable special styling for the first column.
     * If no styling is needed on the first column, this may be configured as `null`.
     */
    firstCls: Ext.baseCSSPrefix + 'grid-cell-first',

    /**
     * @cfg {String} [lastCls='x-grid-cell-last']
     * A CSS class to add to the *last* cell in every row to enable special styling for the last column.
     * If no styling is needed on the last column, this may be configured as `null`.
     */
    lastCls: Ext.baseCSSPrefix + 'grid-cell-last',

    headerRowSelector: 'tr.' + Ext.baseCSSPrefix + 'grid-header-row',

    selectedItemCls: Ext.baseCSSPrefix + 'grid-row-selected',
    beforeSelectedItemCls: Ext.baseCSSPrefix + 'grid-row-before-selected',
    selectedCellCls: Ext.baseCSSPrefix + 'grid-cell-selected',
    focusedItemCls: Ext.baseCSSPrefix + 'grid-row-focused',
    beforeFocusedItemCls: Ext.baseCSSPrefix + 'grid-row-before-focused',
    tableFocusedFirstCls: Ext.baseCSSPrefix + 'grid-table-focused-first',
    tableSelectedFirstCls: Ext.baseCSSPrefix + 'grid-table-selected-first',
    tableOverFirstCls: Ext.baseCSSPrefix + 'grid-table-over-first',
    overItemCls: Ext.baseCSSPrefix + 'grid-row-over',
    beforeOverItemCls: Ext.baseCSSPrefix + 'grid-row-before-over',
    altRowCls:   Ext.baseCSSPrefix + 'grid-row-alt',
    dirtyCls: Ext.baseCSSPrefix + 'grid-dirty-cell',
    rowClsRe: new RegExp('(?:^|\\s*)' + Ext.baseCSSPrefix + 'grid-row-(first|last|alt)(?:\\s+|$)', 'g'),
    cellRe: new RegExp(Ext.baseCSSPrefix + 'grid-cell-headerId-([^\\s]+)(?:\\s|$)', ''),
    positionBody: true,

    // cfg docs inherited
    trackOver: true,

    /**
     * Override this function to apply custom CSS classes to rows during rendering. This function should return the
     * CSS class name (or empty string '' for none) that will be added to the row's wrapping div. To apply multiple
     * class names, simply return them space-delimited within the string (e.g. 'my-class another-class').
     * Example usage:
     *
     *     viewConfig: {
     *         getRowClass: function(record, rowIndex, rowParams, store){
     *             return record.get("valid") ? "row-valid" : "row-error";
     *         }
     *     }
     *
     * @param {Ext.data.Model} record The record corresponding to the current row.
     * @param {Number} index The row index.
     * @param {Object} rowParams **DEPRECATED.** For row body use the
     * {@link Ext.grid.feature.RowBody#getAdditionalData getAdditionalData} method of the rowbody feature.
     * @param {Ext.data.Store} store The store this grid is bound to
     * @return {String} a CSS class name to add to the row.
     * @method
     */
    getRowClass: null,

    /**
     * @cfg {Boolean} stripeRows
     * True to stripe the rows.
     *
     * This causes the CSS class **`x-grid-row-alt`** to be added to alternate rows of
     * the grid. A default CSS rule is provided which sets a background color, but you can override this
     * with a rule which either overrides the **background-color** style using the `!important`
     * modifier, or which uses a CSS selector of higher specificity.
     */
    stripeRows: true,

    /**
     * @cfg {Boolean} markDirty
     * True to show the dirty cell indicator when a cell has been modified.
     */
    markDirty : true,

    /**
     * @cfg {Boolean} enableTextSelection
     * True to enable text selections.
     */
     
    ariaRole: 'grid',

    /**
     * @private
     * Simple initial tpl for TableView just to satisfy the validation within AbstractView.initComponent.
     */
    tpl: '{%values.view.tableTpl.applyOut(values, out)%}',

    tableTpl: [
        '{%',
            'var view=values.view,tableCls="' + Ext.baseCSSPrefix + '" + view.id + "-table ' + Ext.baseCSSPrefix + 'grid-table";',
        '%}',
        '<table id="{view.id}-table" class="{[tableCls]}" border="0" cellspacing="0" cellpadding="0" style="{tableStyle}" {ariaTableAttr}>',
            '{[view.renderColumnSizer(out)]}',
            '{[view.renderTHead(values, out)]}',
            '{[view.renderTFoot(values, out)]}',
            '<tbody id="{view.id}-body" {ariaTbodyAttr}>',
            '{%',
                'view.renderRows(values.rows, values.viewStartIndex, out);',
            '%}',
            '</tbody>',
        '</table>',
        {
            priority: 0
        }
    ],

    rowTpl: [
        '{%',
            'var dataRowCls = values.recordIndex === -1 ? "" : " ' + Ext.baseCSSPrefix + 'grid-data-row";',
        '%}',
        '<tr {[values.rowId ? ("id=\\"" + values.rowId + "\\"") : ""]} ',
            'data-boundView="{view.id}" ',
            'data-recordId="{record.internalId}" ',
            'data-recordIndex="{recordIndex}" ',
            'class="{[values.itemClasses.join(" ")]} {[values.rowClasses.join(" ")]}{[dataRowCls]}" ',
            '{rowAttr:attributes} tabIndex="-1" {ariaRowAttr}>',
            '<tpl for="columns">' +
                '{%',
                    'parent.view.renderCell(values, parent.record, parent.recordIndex, parent.rowIndex, xindex - 1, out, parent)',
                 '%}',
            '</tpl>',
        '</tr>',
        {
            priority: 0
        }
    ],

    cellTpl: [
        '<td class="{tdCls}" {tdAttr} {[Ext.aria ? "id=\\"" + Ext.id() + "\\"" : ""]} {ariaCellAttr}>',
            '<div {unselectableAttr} class="' + Ext.baseCSSPrefix + 'grid-cell-inner {innerCls}"',
                'style="text-align:{align};<tpl if="style">{style}</tpl>" {ariaCellInnerAttr}>{value}</div>',
        '</td>', {
            priority: 0
        }
    ],

    /**
     * @private
     * Flag to disable refreshing SelectionModel on view refresh. Table views render rows with selected CSS class already added if necessary.
     */
    refreshSelmodelOnRefresh: false,

    tableValues: {},

    // Private properties used during the row and cell render process.
    // They are allocated here on the prototype, and cleared/re-used to avoid GC churn during repeated rendering.
    rowValues: {
        itemClasses: [],
        rowClasses: []
    },
    cellValues: {
        classes: [
            Ext.baseCSSPrefix + 'grid-cell ' + Ext.baseCSSPrefix + 'grid-td' // for styles shared between cell and rowwrap
        ]
    },

    /// Private used for buffered rendering
    renderBuffer: document.createElement('div'),

    constructor: function(config) {
        // Adjust our base class if we are inside a TreePanel
        if (config.grid.isTree) {
            config.baseCls = Ext.baseCSSPrefix + 'tree-view';
        }
        this.callParent([config]);
    },

    initComponent: function() {
        var me = this,
            scroll = me.scroll;

        this.addEvents(
            /**
             * @event beforecellclick
             * Fired before the cell click is processed. Return false to cancel the default action.
             * @param {Ext.view.Table} this
             * @param {HTMLElement} td The TD element for the cell.
             * @param {Number} cellIndex
             * @param {Ext.data.Model} record
             * @param {HTMLElement} tr The TR element for the cell.
             * @param {Number} rowIndex
             * @param {Ext.EventObject} e
             */
            'beforecellclick',
            /**
             * @event cellclick
             * Fired when table cell is clicked.
             * @param {Ext.view.Table} this
             * @param {HTMLElement} td The TD element for the cell.
             * @param {Number} cellIndex
             * @param {Ext.data.Model} record
             * @param {HTMLElement} tr The TR element for the cell.
             * @param {Number} rowIndex
             * @param {Ext.EventObject} e
             */
            'cellclick',
            /**
             * @event beforecelldblclick
             * Fired before the cell double click is processed. Return false to cancel the default action.
             * @param {Ext.view.Table} this
             * @param {HTMLElement} td The TD element for the cell.
             * @param {Number} cellIndex
             * @param {Ext.data.Model} record
             * @param {HTMLElement} tr The TR element for the cell.
             * @param {Number} rowIndex
             * @param {Ext.EventObject} e
             */
            'beforecelldblclick',
            /**
             * @event celldblclick
             * Fired when table cell is double clicked.
             * @param {Ext.view.Table} this
             * @param {HTMLElement} td The TD element for the cell.
             * @param {Number} cellIndex
             * @param {Ext.data.Model} record
             * @param {HTMLElement} tr The TR element for the cell.
             * @param {Number} rowIndex
             * @param {Ext.EventObject} e
             */
            'celldblclick',
            /**
             * @event beforecellcontextmenu
             * Fired before the cell right click is processed. Return false to cancel the default action.
             * @param {Ext.view.Table} this
             * @param {HTMLElement} td The TD element for the cell.
             * @param {Number} cellIndex
             * @param {Ext.data.Model} record
             * @param {HTMLElement} tr The TR element for the cell.
             * @param {Number} rowIndex
             * @param {Ext.EventObject} e
             */
            'beforecellcontextmenu',
            /**
             * @event cellcontextmenu
             * Fired when table cell is right clicked.
             * @param {Ext.view.Table} this
             * @param {HTMLElement} td The TD element for the cell.
             * @param {Number} cellIndex
             * @param {Ext.data.Model} record
             * @param {HTMLElement} tr The TR element for the cell.
             * @param {Number} rowIndex
             * @param {Ext.EventObject} e
             */
            'cellcontextmenu',
            /**
             * @event beforecellmousedown
             * Fired before the cell mouse down is processed. Return false to cancel the default action.
             * @param {Ext.view.Table} this
             * @param {HTMLElement} td The TD element for the cell.
             * @param {Number} cellIndex
             * @param {Ext.data.Model} record
             * @param {HTMLElement} tr The TR element for the cell.
             * @param {Number} rowIndex
             * @param {Ext.EventObject} e
             */
            'beforecellmousedown',
            /**
             * @event cellmousedown
             * Fired when the mousedown event is captured on the cell.
             * @param {Ext.view.Table} this
             * @param {HTMLElement} td The TD element for the cell.
             * @param {Number} cellIndex
             * @param {Ext.data.Model} record
             * @param {HTMLElement} tr The TR element for the cell.
             * @param {Number} rowIndex
             * @param {Ext.EventObject} e
             */
            'cellmousedown',
            /**
             * @event beforecellmouseup
             * Fired before the cell mouse up is processed. Return false to cancel the default action.
             * @param {Ext.view.Table} this
             * @param {HTMLElement} td The TD element for the cell.
             * @param {Number} cellIndex
             * @param {Ext.data.Model} record
             * @param {HTMLElement} tr The TR element for the cell.
             * @param {Number} rowIndex
             * @param {Ext.EventObject} e
             */
            'beforecellmouseup',
            /**
             * @event cellmouseup
             * Fired when the mouseup event is captured on the cell.
             * @param {Ext.view.Table} this
             * @param {HTMLElement} td The TD element for the cell.
             * @param {Number} cellIndex
             * @param {Ext.data.Model} record
             * @param {HTMLElement} tr The TR element for the cell.
             * @param {Number} rowIndex
             * @param {Ext.EventObject} e
             */
            'cellmouseup',
            /**
             * @event beforecellkeydown
             * Fired before the cell key down is processed. Return false to cancel the default action.
             * @param {Ext.view.Table} this
             * @param {HTMLElement} td The TD element for the cell.
             * @param {Number} cellIndex
             * @param {Ext.data.Model} record
             * @param {HTMLElement} tr The TR element for the cell.
             * @param {Number} rowIndex
             * @param {Ext.EventObject} e
             */
            'beforecellkeydown',
            /**
             * @event cellkeydown
             * Fired when the keydown event is captured on the cell.
             * @param {Ext.view.Table} this
             * @param {HTMLElement} td The TD element for the cell.
             * @param {Number} cellIndex
             * @param {Ext.data.Model} record
             * @param {HTMLElement} tr The TR element for the cell.
             * @param {Number} rowIndex
             * @param {Ext.EventObject} e
             */
            'cellkeydown'
        );

        /**
         * @private
         * @property {Ext.dom.AbstractElement.Fly} body
         * A flyweight Ext.Element which encapsulates a reference to the view's main row containing element.
         * *Note that the `dom` reference will not be present until the first data refresh*
         */
        me.body = new Ext.dom.Element.Fly();
        me.body.id = me.id + 'gridBody';

        // Scrolling within a TableView is controlled by the scroll config of its owning GridPanel
        // It must see undefined in this property in order to leave the scroll styles alone at afterRender time
        me.autoScroll = undefined;

        // If trackOver has been turned off, null out the overCls because documented behaviour
        // in AbstractView is to turn trackOver on if overItemCls is set.
        if (!me.trackOver) {
            me.overItemCls = null;
            me.beforeOverItemCls = null;
        }

        // Convert grid scroll config to standard Component scrolling configurations.
        if (scroll === true || scroll === 'both') {
            me.autoScroll = true;
        } else if (scroll === 'horizontal') {
            me.overflowX = 'auto';
        } else if (scroll === 'vertical') {
            me.overflowY = 'auto';
        }
        me.selModel.view = me.headerCt.view = me;

        // Features need a reference to the grid.
        // Grid needs an immediate reference to its view so that the view can reliably be got from the grid during initialization
        me.grid.view = me;
        me.initFeatures(me.grid);
        delete me.grid;

        me.itemSelector = me.getItemSelector();
        me.all = new Ext.view.NodeCache(me);
        me.callParent();
    },

    getVisibleColumnManager: function(){
        var owner = this.ownerCt,
            lock = owner.ownerLockable;

        return lock ? lock.getVisibleColumnManager() : owner.getVisibleColumnManager();

    },

    /**
     * @private
     * Move a grid column from one position to another
     * @param {Number} fromIdx The index from which to move columns
     * @param {Number} toIdx The index at which to insert columns.
     * @param {Number} [colsToMove=1] The number of columns to move beginning at the `fromIdx`
     */
    moveColumn: function(fromIdx, toIdx, colsToMove) {
        var me = this,
            fragment = (colsToMove > 1) ? document.createDocumentFragment() : undefined,
            destinationCellIdx = toIdx,
            colCount = me.getGridColumns().length,
            lastIndex = colCount - 1,
            doFirstLastClasses = (me.firstCls || me.lastCls) && (toIdx === 0 || toIdx == colCount || fromIdx === 0 || fromIdx == lastIndex),
            i,
            j,
            rows, len, tr, cells,
            tables;

        // Dragging between locked and unlocked side first refreshes the view, and calls onHeaderMoved with
        // fromIndex and toIndex the same.
        if (me.rendered && toIdx !== fromIdx) {
            // Grab all rows which have column cells in.
            // That is data rows and column sizing rows.
            rows = me.el.query(me.getDataRowSelector());

            if (toIdx > fromIdx && fragment) {
                destinationCellIdx -= colsToMove;
            }

            for (i = 0, len = rows.length; i < len; i++) {
                tr = rows[i];
                cells = tr.childNodes;

                // Keep first cell class and last cell class correct *only if needed*
                if (doFirstLastClasses) {

                    if (cells.length === 1) {
                        Ext.fly(cells[0]).addCls(me.firstCls);
                        Ext.fly(cells[0]).addCls(me.lastCls);
                        continue;
                    }
                    if (fromIdx === 0) {
                        Ext.fly(cells[0]).removeCls(me.firstCls);
                        Ext.fly(cells[1]).addCls(me.firstCls);
                    } else if (fromIdx === lastIndex) {
                        Ext.fly(cells[lastIndex]).removeCls(me.lastCls);
                        Ext.fly(cells[lastIndex - 1]).addCls(me.lastCls);
                    }
                    if (toIdx === 0) {
                        Ext.fly(cells[0]).removeCls(me.firstCls);
                        Ext.fly(cells[fromIdx]).addCls(me.firstCls);
                    } else if (toIdx === colCount) {
                        Ext.fly(cells[lastIndex]).removeCls(me.lastCls);
                        Ext.fly(cells[fromIdx]).addCls(me.lastCls);
                    }
                }

                if (fragment) {
                    for (j = 0; j < colsToMove; j++) {
                        fragment.appendChild(cells[fromIdx]);
                    }
                    tr.insertBefore(fragment, cells[destinationCellIdx] || null);
                } else {
                    tr.insertBefore(cells[fromIdx], cells[destinationCellIdx] || null);
                }
            }

            // Shuffle the <colgroup> elements at the ta=op of all <tables> in the grid
            tables = me.el.query(me.getBodySelector());
            for (i = 0, len = tables.length; i < len; i++) {
                tr = tables[i];
                if (fragment) {
                    for (j = 0; j < colsToMove; j++) {
                        fragment.appendChild(tr.childNodes[fromIdx]);
                    }
                    tr.insertBefore(fragment, tr.childNodes[destinationCellIdx] || null);
                } else {
                    tr.insertBefore(tr.childNodes[fromIdx], tr.childNodes[destinationCellIdx] || null);
                }
            }
        }
    },

    // scroll the view to the top
    scrollToTop: Ext.emptyFn,

    /**
     * Add a listener to the main view element. It will be destroyed with the view.
     * @private
     */
    addElListener: function(eventName, fn, scope){
        this.mon(this, eventName, fn, scope, {
            element: 'el'
        });
    },

    /**
     * Get the leaf columns used for rendering the grid rows.
     * @private
     */
    getGridColumns: function() {
        return this.ownerCt.getVisibleColumnManager().getColumns();
    },

    /**
     * Get a leaf level header by index regardless of what the nesting
     * structure is.
     * @private
     * @param {Number} index The index
     */
    getHeaderAtIndex: function(index) {
        return this.ownerCt.getVisibleColumnManager().getHeaderAtIndex(index);
    },

    /**
     * Get the cell (td) for a particular record and column.
     * @param {Ext.data.Model} record
     * @param {Ext.grid.column.Column} column
     * @private
     */
    getCell: function(record, column) {
        var row = this.getNode(record, true);
        return Ext.fly(row).down(column.getCellSelector());
    },

    /**
     * Get a reference to a feature
     * @param {String} id The id of the feature
     * @return {Ext.grid.feature.Feature} The feature. Undefined if not found
     */
    getFeature: function(id) {
        var features = this.featuresMC;
        if (features) {
            return features.get(id);
        }
    },

    // @private
    // Finds a features by ftype in the features array
    findFeature: function(ftype) {
        if (this.features) {
            return Ext.Array.findBy(this.features, function(feature) {
                if (feature.ftype === ftype) {
                    return true;
                }
            });
        }
    },

    /**
     * Initializes each feature and bind it to this view.
     * @private
     */
    initFeatures: function(grid) {
        var me = this,
            i,
            features,
            feature,
            len;

        me.tableTpl = Ext.XTemplate.getTpl(this, 'tableTpl');
        me.rowTpl   = Ext.XTemplate.getTpl(this, 'rowTpl');
        me.cellTpl  = Ext.XTemplate.getTpl(this, 'cellTpl');

        me.featuresMC = new Ext.util.MixedCollection();
        features = me.features = me.constructFeatures();
        len = features ? features.length : 0;
        for (i = 0; i < len; i++) {
            feature = features[i];

            // inject a reference to view and grid - Features need both
            feature.view = me;
            feature.grid = grid;
            me.featuresMC.add(feature);
            feature.init(grid);
        }
    },

    renderTHead: function(values, out) {
        var headers = values.view.headerFns,
            len, i;

        if (headers) {
            for (i = 0, len = headers.length; i < len; ++i) {
                headers[i].call(this, values, out);
            }
        }
    },

    // Currently, we don't have ordering support for header/footer functions,
    // they will be pushed on at construction time. If the need does arise,
    // we can add this functionality in the future, but for now it's not
    // really necessary since currently only the summary feature uses this.
    addHeaderFn: function(){
        var headers = this.headerFns;
        if (!headers) {
            headers = this.headerFns = [];
        }
        headers.push(fn);
    },

    renderTFoot: function(values, out){
        var footers = values.view.footerFns,
            len, i;

        if (footers) {
            for (i = 0, len = footers.length; i < len; ++i) {
                footers[i].call(this, values, out);
            }
        }
    },

    addFooterFn: function(fn){
        var footers = this.footerFns;
        if (!footers) {
            footers = this.footerFns = [];
        }
        footers.push(fn);
    },

    addTableTpl: function(newTpl) {
        return this.addTpl('tableTpl', newTpl);
    },

    addRowTpl: function(newTpl) {
        return this.addTpl('rowTpl', newTpl);
    },

    addCellTpl: function(newTpl) {
        return this.addTpl('cellTpl', newTpl);
    },

    addTpl: function(which, newTpl) {
        var me = this,
            tpl,
            prevTpl;

        newTpl = Ext.Object.chain(newTpl);

        // If we have been passed an object of the form
        // {
        //      before: fn
        //      after: fn
        // }
        if (!newTpl.isTemplate) {
            newTpl.applyOut = me.tplApplyOut;
        }

        // Stop at the first TPL who's priority is less than the passed rowTpl
        for (tpl = me[which]; newTpl.priority < tpl.priority; tpl = tpl.nextTpl) {
            prevTpl = tpl;
        }

        // If we had skipped over some, link the previous one to the passed rowTpl
        if (prevTpl) {
            prevTpl.nextTpl = newTpl;
        }
        // First one
        else {
            me[which] = newTpl;
        }
        newTpl.nextTpl = tpl;
        return newTpl;
    },

    tplApplyOut: function(values, out) {
        if (this.before) {
            if (this.before(values, out) === false) {
                return;
            }
        }
        this.nextTpl.applyOut(values, out);
        if (this.after) {
            this.after(values, out);
        }
    },

    /**
     * @private
     * Converts the features array as configured, into an array of instantiated Feature objects.
     *
     * Must have no side effects other than Feature instantiation.
     *
     * MUST NOT update the this.features property, and MUST NOT update the instantiated Features.
     */
    constructFeatures: function() {
        var me = this,
            features = me.features,
            feature,
            result,
            i = 0, len;

        if (features) {
            result = [];
            len = features.length;
            for (; i < len; i++) {
                feature = features[i];
                if (!feature.isFeature) {
                    feature = Ext.create('feature.' + feature.ftype, feature);
                }
                result[i] = feature;
            }
        }
        return result;
    },

    beforeRender: function() {
        var me = this;
        me.callParent();

        if (!me.enableTextSelection) {
            me.protoEl.unselectable();
        }
    },

    // Private template method implemented starting at the AbstractView class.
    onViewScroll: function(e, t) {
        // We ignore scrolling caused by focusing
        if (!this.ignoreScroll) {
            this.callParent(arguments);
            this.fireEvent('bodyscroll', e, t);
        }
    },

    // private
    // Create the DOM element which enapsulates the passed record.
    // Used when updating existing rows, so drills down into resulting structure .
    createRowElement: function(record, index) {
        var me = this,
            div = me.renderBuffer;

        me.tpl.overwrite(div, me.collectData([record], index));
        // Return first element within node containing element
        return Ext.fly(div).down(me.getNodeContainerSelector(), true).firstChild;
    },

    // private
    // Override so that we can use a quicker way to access the row nodes.
    // They are simply all child nodes of the TBODY element.
    bufferRender: function(records, index) {
        var me = this,
            div = me.renderBuffer;

        me.tpl.overwrite(div, me.collectData(records, index));
        return Ext.Array.toArray(Ext.fly(div).down(me.getNodeContainerSelector(), true).childNodes);
    },

    collectData: function(records, startIndex) {
        this.rowValues.view = this;

        return {
            view: this,
            rows: records,
            viewStartIndex: startIndex,
            tableStyle: this.bufferedRenderer ? ('position:absolute;top:' + this.bufferedRenderer.bodyTop) : ''
        };
    },

    // Overridden implementation.
    // Called by refresh to collect the view item nodes.
    // Note that these may be wrapping rows which *contain* rows which map to records
    collectNodes: function(targetEl) {
        this.all.fill(this.getNodeContainer().childNodes, this.all.startIndex);
    },

    // Private. Called when the table changes height.
    // For example, see examples/grid/group-summary-grid.html
    // If we have flexed column headers, we need to update the header layout
    // because it may have to accommodate (or cease to accommodate) a vertical scrollbar.
    // Only do this on platforms which have a space-consuming scrollbar.
    // Only do it when vertical scrolling is enabled.
    refreshSize: function() {
        var me = this,
            grid,
            bodySelector = me.getBodySelector();

        // On every update of the layout system due to data update, capture the view's main element in our private flyweight.
        // IF there *is* a main element. Some TplFactories emit naked rows.
        if (bodySelector) {
            me.body.attach(me.el.child(bodySelector, true));
        }

        if (!me.hasLoadingHeight) {
            grid = me.up('tablepanel');

            // Suspend layouts in case the superclass requests a layout. We might too, so they
            // must be coalescsed.
            Ext.suspendLayouts();

            me.callParent();

            // Since columns and tables are not sized by generated CSS rules any more, EVERY table refresh
            // has to be followed by a layout to ensure correct table and column sizing.
            grid.updateLayout();

            Ext.resumeLayouts(true);
        }
    },

    statics: {
        getBoundView: function(node) {
            return Ext.getCmp(node.getAttribute('data-boundView'));
        }
    },

    getRecord: function(node) {
        var me = this,
            recordIndex;

        // If store.destroyStore has been called before some delayed event fires on a node, we must ignore the event.
        if (me.store.isDestroyed) {
            return;
        }

        node = me.getNode(node);
        if (node) {
            // If we're grouping, the indexes may be off
            // because of collapsed groups, so just grab it by id
            if (!me.hasActiveGrouping()) {
                recordIndex = node.getAttribute('data-recordIndex');
                if (recordIndex) {
                    recordIndex = parseInt(recordIndex, 10);
                    if (recordIndex > -1) {
                        // The index is the index in the original Store, not in a GroupStore
                        // The Grouping Feature increments the index to skip over unrendered records in collapsed groups
                        return me.store.data.getAt(recordIndex);
                    }
                }
            }
            return me.store.getByInternalId(node.getAttribute('data-recordId'));
        }
    },

    indexOf: function(node) {
        node = this.getNode(node, false);
        if (!node && node !== 0) {
            return -1;
        }
        return this.all.indexOf(node);
    },

    indexInStore: function(node) {
        node = node.isCollapsedPlaceholder ? this.getNode(node) : this.getNode(node, false);
        if (!node && node !== 0) {
            return -1;
        }
        var recordIndex = node.getAttribute('data-recordIndex');
        if (recordIndex) {
            return parseInt(recordIndex, 10);
        }
        return this.dataSource.indexOf(this.getRecord(node));
    },

    renderRows: function(rows, viewStartIndex, out) {
        var rowValues = this.rowValues,
            rowCount = rows.length,
            i;

        rowValues.view = this;
        rowValues.columns = this.ownerCt.getVisibleColumnManager().getColumns();

        for (i = 0; i < rowCount; i++, viewStartIndex++) {
            rowValues.itemClasses.length = rowValues.rowClasses.length = 0;
            this.renderRow(rows[i], viewStartIndex, out);
        }

        // Dereference objects since rowValues is a persistent on our prototype
        rowValues.view = rowValues.columns = rowValues.record = null;
    },

    /* Alternative column sizer element renderer.
    renderTHeadColumnSizer: function(out) {
        var columns = this.getGridColumns(),
            len = columns.length, i,
            column, width;

        out.push('<thead><tr class="' + Ext.baseCSSPrefix + 'grid-header-row">');
        for (i = 0; i < len; i++) {
            column = columns[i];
            width = column.hidden ? 0 : (column.lastBox ? column.lastBox.width : Ext.grid.header.Container.prototype.defaultWidth);
            out.push('<th class="', Ext.baseCSSPrefix, 'grid-cell-', columns[i].getItemId(), '" style="width:' + width + 'px"></th>');
        }
        out.push('</tr></thead>');
    },
    */

    renderColumnSizer: function(out) {
        var columns = this.getGridColumns(),
            len = columns.length, i,
            column, width;

        for (i = 0; i < len; i++) {
            column = columns[i];
            width = column.hidden ? 0 : (column.lastBox ? column.lastBox.width : Ext.grid.header.Container.prototype.defaultWidth);
            out.push('<colgroup role="presentation"><col role="presentation" class="', columns[i].getCellId(), '" style="width:' + width + 'px"></colgroup>');
        }
    },

    /**
     * @private
     * Renders the HTML markup string for a single row into the passed array as a sequence of strings, or
     * returns the HTML markup for a single row.
     *
     * @param {Ext.data.Model} record The record to render.
     * @param {String[]} [out] A string array onto which to append the resulting HTML string. If omitted,
     * the resulting HTML string is returned.
     * @return {String} **only when the out parameter is omitted** The resulting HTML string.
     */
    renderRow: function(record, rowIdx, out) {
        var me = this,
            isMetadataRecord = rowIdx === -1,
            selModel = me.selModel,
            rowValues = me.rowValues,
            itemClasses = rowValues.itemClasses,
            rowClasses = rowValues.rowClasses,
            cls,
            rowTpl = me.rowTpl;

        // Set up mandatory properties on rowValues
        rowValues.record = record;
        rowValues.recordId = record.internalId;

        // recordIndex is index in true store (NOT the data source - possibly a GroupStore)
        rowValues.recordIndex = me.store.indexOf(record);

        // rowIndex is the row number in the view.
        rowValues.rowIndex = rowIdx;
        rowValues.rowId = me.getRowId(record);
        rowValues.itemCls = rowValues.rowCls = '';
        if (!rowValues.columns) {
            rowValues.columns = me.ownerCt.getVisibleColumnManager().getColumns();
        }

        itemClasses.length = rowClasses.length = 0;

        // If it's a metadata record such as a summary record.
        // So do not decorate it with the regular CSS.
        // The Feature which renders it must know how to decorate it.
        if (!isMetadataRecord) {
            itemClasses[0] = Ext.baseCSSPrefix + "grid-row";

            if (!me.ownerCt.disableSelection && selModel.isRowSelected) {
                // Selection class goes on the outermost row, so it goes into itemClasses
                if (selModel.isRowSelected(record)) {
                    itemClasses.push(me.selectedItemCls);
                }
                /* TODO: When merging into sencha-5.0.x branch. These classes should be pushed into the itemClasses array to go on the outermost view item element */
                // Ensure selection class is added to selected records, and to the record *before* any selected record
                // When looking ahead to see if the next record is selected, ensure we do not look past the end!
                if (me.rowValues.recordIndex < me.store.getTotalCount() - 1 && selModel.isRowSelected(me.rowValues.recordIndex + 1) && !me.isRowStyleFirst(rowIdx + 1)) {
                    rowClasses.push(me.beforeSelectedItemCls);
                }
            }

            if (me.stripeRows && rowIdx % 2 !== 0) {
                rowClasses.push(me.altRowCls);
            }

            if (me.getRowClass) {
                cls = me.getRowClass(record, rowIdx, null, me.dataSource);
                if (cls) {
                    rowClasses.push(cls);
                }
            }
        }

        if (out) {
            rowTpl.applyOut(rowValues, out);
        } else {
            return rowTpl.apply(rowValues);
        }
    },

    /**
     * @private
     * Emits the HTML representing a single grid cell into the passed output stream (which is an array of strings).
     *
     * @param {Ext.grid.column.Column} column The column definition for which to render a cell.
     * @param {Number} recordIndex The row index (zero based within the {@link #store}) for which to render the cell.
     * @param {Number} rowIndex The row index (zero based within this view for which to render the cell.
     * @param {Number} columnIndex The column index (zero based) for which to render the cell.
     * @param {String[]} out The output stream into which the HTML strings are appended.
     */
    renderCell: function(column, record, recordIndex, rowIndex, columnIndex, out) {
        var me = this,
            selModel = me.selModel,
            cellValues = me.cellValues,
            classes = cellValues.classes,
            fieldValue = record.data[column.dataIndex],
            cellTpl = me.cellTpl,
            fullIndex, value, clsInsertPoint;

        cellValues.record = record;
        cellValues.column = column;
        cellValues.recordIndex = recordIndex;
        cellValues.rowIndex = rowIndex;
        cellValues.columnIndex = columnIndex;
        cellValues.cellIndex = columnIndex;
        cellValues.align = column.align;
        cellValues.tdCls = column.tdCls;
        cellValues.innerCls = column.innerCls;
        cellValues.style = cellValues.tdAttr = "";
        cellValues.unselectableAttr = me.enableTextSelection ? '' : 'unselectable="on"';

        if (column.renderer && column.renderer.call) {
            fullIndex = me.ownerCt.columnManager.getHeaderIndex(column);
            value = column.renderer.call(column.scope || me.ownerCt, fieldValue, cellValues, record, recordIndex, fullIndex, me.dataSource, me);
            if (cellValues.css) {
                // This warning attribute is used by the compat layer
                // TODO: remove when compat layer becomes deprecated
                record.cssWarning = true;
                cellValues.tdCls += ' ' + cellValues.css;
                delete cellValues.css;
            }
        } else {
            value = fieldValue;
        }
        cellValues.value = (value == null || value === '') ? '&#160;' : value;

        // Calculate classes to add to cell
        classes[1] = column.getCellId();

        // On IE8, array[len] = 'foo' is twice as fast as array.push('foo')
        // So keep an insertion point and use assignment to help IE!
        clsInsertPoint = 2;

        if (column.tdCls) {
            classes[clsInsertPoint++] = column.tdCls;
        }
        if (me.markDirty && record.isModified(column.dataIndex)) {
            classes[clsInsertPoint++] = me.dirtyCls;
        }
        if (column.isFirstVisible) {
            classes[clsInsertPoint++] = me.firstCls;
        }
        if (column.isLastVisible) {
            classes[clsInsertPoint++] = me.lastCls;
        }
        if (!me.enableTextSelection) {
            classes[clsInsertPoint++] = me.unselectableCls;
        }
        if (cellValues.tdCls) {
            classes[clsInsertPoint++] = cellValues.tdCls;
        }
        if (selModel && selModel.isCellModel && selModel.isCellSelected(me, recordIndex, columnIndex)) {
            classes[clsInsertPoint++] = (me.selectedCellCls);
        }

        // Chop back array to only what we've set
        classes.length = clsInsertPoint;

        cellValues.tdCls = classes.join(' ');

        cellTpl.applyOut(cellValues, out);

        // Dereference objects since cellValues is a persistent var in the XTemplate's scope chain
        cellValues.column = null;
    },

    /**
     * Returns the node given the passed Record, or index or node.
     * @param {HTMLElement/String/Number/Ext.data.Model} nodeInfo The node or record
     * @param {Boolean} [dataRow] `true` to return the data row (not the top level row if wrapped), `false`
     * to return the top level row.
     * @return {HTMLElement} The node or null if it wasn't found
     */
    getNode: function(nodeInfo, dataRow) {
        var fly,
            result = this.callParent(arguments);

        if (result && result.tagName) {
            if (dataRow) {
                if (!(fly = Ext.fly(result)).is(this.dataRowSelector)) {
                    return fly.down(this.dataRowSelector, true);
                }
            } else if (dataRow === false) {
                if (!(fly = Ext.fly(result)).is(this.itemSelector)) {
                    return fly.up(this.itemSelector, null, true);
                }
            }
        }
        return result;
    },

    getRowId: function(record){
        return this.id + '-record-' + record.internalId;
    },

    constructRowId: function(internalId){
        return this.id + '-record-' + internalId;
    },

    getNodeById: function(id, dataRow){
        id = this.constructRowId(id);
        return this.retrieveNode(id, dataRow);
    },

    getNodeByRecord: function(record, dataRow) {
        var id = this.getRowId(record);
        return this.retrieveNode(id, dataRow);
    },

    retrieveNode: function(id, dataRow){
        var result = this.el.getById(id, true),
            itemSelector = this.itemSelector,
            fly;

        if (dataRow === false && result) {
            if (!(fly = Ext.fly(result)).is(itemSelector)) {
                return fly.up(itemSelector, null, true);
            }
        }
        return result;
    },

    // Links back from grid rows are installed by the XTemplate as data attributes
    updateIndexes: Ext.emptyFn,

    // Outer table
    bodySelector: 'table',

    // Element which contains rows
    nodeContainerSelector: 'tbody',

    // view item (may be a wrapper)
    itemSelector: 'tr.' + Ext.baseCSSPrefix + 'grid-row',

    // row which contains cells as opposed to wrapping rows
    dataRowSelector: 'tr.' + Ext.baseCSSPrefix + 'grid-data-row',

    // cell
    cellSelector: 'td.' + Ext.baseCSSPrefix + 'grid-cell',

    // `<column sizer>`
    sizerSelector: 'col.' + Ext.baseCSSPrefix + 'grid-cell-headerId',

    innerSelector: 'div.' + Ext.baseCSSPrefix + 'grid-cell-inner',

    getNodeContainer: function() {
        return this.el.down(this.nodeContainerSelector, true);
    },

    /**
     * Returns a CSS selector which selects the outermost element(s) in this view.
     */
    getBodySelector: function() {
        return this.bodySelector + '.' + Ext.baseCSSPrefix + this.id + '-table';
    },

    /**
     * Returns a CSS selector which selects the element which contains record nodes.
     */
    getNodeContainerSelector: function() {
        return this.nodeContainerSelector;
    },

    /**
     * Returns a CSS selector which selects the element(s) which define the width of a column.
     *
     * This is used by the {@link Ext.view.TableLayout} when resizing columns.
     *
     */
    getColumnSizerSelector: function(header) {
        return this.sizerSelector + '-' + header.getItemId();
    },

    /**
     * Returns a CSS selector which selects items of the view rendered by the rowTpl
     */
    getItemSelector: function() {
        return this.itemSelector;
    },

    /**
     * Returns a CSS selector which selects a row which contains cells.
     *
     * These *may not* correspond to actual records in the store. This selector may be used
     * to identify things like total rows or header rows as injected by features which modify
     * the rowTpl.
     *
     */
    getDataRowSelector: function() {
        return this.dataRowSelector;
    },

    /**
     * Returns a CSS selector which selects a particular column if the desired header is passed,
     * or a general cell selector is no parameter is passed.
     *
     * @param {Ext.grid.column.Column} [header] The column for which to return the selector. If
     * omitted, the general cell selector which matches **ant cell** will be returned.
     *
     */
    getCellSelector: function(header) {
        return header ? header.getCellSelector() : this.cellSelector; 
    },

    /*
     * Returns a CSS selector which selects the content carrying element within cells.
     */
    getCellInnerSelector: function(header) {
        return this.getCellSelector(header) + ' ' + this.innerSelector;
    },

    /**
     * Adds a CSS Class to a specific row.
     * @param {HTMLElement/String/Number/Ext.data.Model} rowInfo An HTMLElement, index or instance of a model
     * representing this row
     * @param {String} cls
     */
    addRowCls: function(rowInfo, cls) {
        var row = this.getNode(rowInfo, false);
        if (row) {
            Ext.fly(row).addCls(cls);
        }
    },

    /**
     * Removes a CSS Class from a specific row.
     * @param {HTMLElement/String/Number/Ext.data.Model} rowInfo An HTMLElement, index or instance of a model
     * representing this row
     * @param {String} cls
     */
    removeRowCls: function(rowInfo, cls) {
        var row = this.getNode(rowInfo, false);
        if (row) {
            Ext.fly(row).removeCls(cls);
        }
    },

    setHighlightedItem: function(item) {
        var me = this,
            highlighted = me.highlightedItem;

        if (highlighted && me.el.isAncestor(highlighted) && me.isRowStyleFirst(highlighted)) {
            me.toggleRowTableCls(highlighted, me.tableOverFirstCls, false);
        }

        item = me.getNode(item, false); // make sure item is not a "data row"

        if (item && me.isRowStyleFirst(item)) {
            me.toggleRowTableCls(item, me.tableOverFirstCls, true);
        }

        me.callParent(arguments);
    },

    // GridSelectionModel invokes onRowSelect as selection changes
    onRowSelect: function(rowIdx) {
        var me = this,
            beforeSelectedItemCls = me.beforeSelectedItemCls;

        me.addRowCls(rowIdx, me.selectedItemCls);
        if (me.isRowStyleFirst(rowIdx)) {
            me.toggleRowTableCls(rowIdx, me.tableSelectedFirstCls, true);
            if (rowIdx > 0) {
                me.removeRowCls(rowIdx - 1, beforeSelectedItemCls);
            }
        } else {
            me.addRowCls(rowIdx - 1, beforeSelectedItemCls);
        }
    },

    // GridSelectionModel invokes onRowDeselect as selection changes
    onRowDeselect: function(rowIdx) {
        var me = this;

        me.removeRowCls(rowIdx, [me.selectedItemCls, me.focusedItemCls]);
        if (me.isRowStyleFirst(rowIdx)) {
            me.toggleRowTableCls(rowIdx, [me.tableFocusedFirstCls, me.tableSelectedFirstCls], false);
        } else {
            me.removeRowCls(rowIdx - 1, [me.beforeFocusedItemCls, me.beforeSelectedItemCls]);
        }
    },

    onCellSelect: function(position) {
        var cell = this.getCellByPosition(position);
        if (cell) {
            cell.addCls(this.selectedCellCls);
        }
    },

    onCellDeselect: function(position) {
        var cell = this.getCellByPosition(position, true);
        if (cell) {
            Ext.fly(cell).removeCls(this.selectedCellCls);
        }

    },

    getCellByPosition: function(position, returnDom) {
        if (position) {
            var row   = this.getNode(position.row, true),
                header = this.ownerCt.getColumnManager().getHeaderAtIndex(position.column);

            if (header && row) {
                return Ext.fly(row).down(this.getCellSelector(header), returnDom);
            }
        }
        return false;
    },

    getFocusEl: function() {
        return this.focusEl;
    },

    // GridSelectionModel invokes onRowFocus to 'highlight'
    // the last row focused
    onRowFocus: function(rowIdx, highlight, supressFocus) {
        var me = this;

        if (highlight) {
            me.addRowCls(rowIdx, me.focusedItemCls);
            if (me.isRowStyleFirst(rowIdx)) {
                me.toggleRowTableCls(rowIdx, me.tableFocusedFirstCls, true);
            } else {
                me.addRowCls(rowIdx - 1, me.beforeFocusedItemCls);
            }
            if (!supressFocus) {
                me.focusRow(rowIdx);
            }
            //this.el.dom.setAttribute('aria-activedescendant', row.id);
        } else {
            me.removeRowCls(rowIdx, me.focusedItemCls);
            if (me.isRowStyleFirst(rowIdx)) {
                me.toggleRowTableCls(rowIdx, me.tableFocusedFirstCls, false);
            } else {
                me.removeRowCls(rowIdx - 1, me.beforeFocusedItemCls);
            }
        }

        if ((Ext.isIE6 || Ext.isIE7) && !me.ownerCt.rowLines) {
            me.repaintRow(rowIdx)
        }
    },

    /**
     * Focuses a particular row and brings it into view. Will fire the rowfocus event.
     * @param {HTMLElement/String/Number/Ext.data.Model} row An HTMLElement template node, index of a template node, the id of a template node or the
     * @param {Boolean/Number} [delay] Delay the focus this number of milliseconds (true for 10 milliseconds).
     * record associated with the node.
     */
    focusRow: function(row, delay) {
        var me = this,
            record,
            focusTask = me.getFocusTask();

        if (delay) {
            focusTask.delay(Ext.isNumber(delay) ? delay : 10, me.focusRow, me, [row, false]);
            return;
        }

        // An immediate focus call must cancel any outstanding delayed focus calls.
        focusTask.cancel();

        // Do not attempt to focus if hidden or within collapsed Panel
        // Maintainer: Note that to avoid an unnecessary call to me.getNode if not visible, or another, nested if test,
        // the assignment of the row var is embedded inside the condition expression.
        if (me.isVisible(true) && (row = me.getNode(row, true))) {
            me.scrollRowIntoView(row);
            record = me.getRecord(row);
            me.selModel.setLastFocused(record);
            me.doFocus(row);
            me.fireEvent('rowfocus', record, row, me.indexInStore(row));
        }
    },

    scrollRowIntoView: function(row) {
        row = this.getNode(row, true);
        if (row) {
            Ext.fly(row).scrollIntoView(this.el, false);
        }
    },

    /**
     * Focuses a particular cell and brings it into view. Will fire the rowfocus event.
     * @param {Ext.grid.CellContext} pos The cell to select
     * @param {Boolean/Number} [delay] Delay the focus this number of milliseconds (true for 10 milliseconds).
     */
    focusCell: function(position, delay) {
        var me = this,
            cell,
            focusTask = me.getFocusTask();

        if (delay) {
            focusTask.delay(Ext.isNumber(delay) ? delay : 10, me.focusCell, me, [position, false]);
            return;
        }

        // An immediate focus call must cancel any outstanding delayed focus calls.
        focusTask.cancel();

        // Do not attempt to focus if hidden or within collapsed Panel
        // Maintainer: Note that to avoid an unnecessary call to me.getCellByPosition if not visible, or another, nested if test,
        // the assignment of the cell var is embedded inside the condition expression.
        if (me.isVisible(true) && (cell = me.getCellByPosition(position))) {
            me.scrollCellIntoView(cell);
            me.doFocus(me.getNode(position.row));
            me.fireEvent('cellfocus', position.record, cell, position);
        }
    },

    // Private
    // Implementation which saves and restores scroll position on IE.
    doFocus: function(rowDom) {
        var me = this,
            saveScroll = Ext.isIE,
            scrollLeft;

        if (saveScroll) {
            scrollLeft = me.el.getScrollLeft();
            me.ignoreScroll = true;
        }

        (me.focusEl = Ext.get(rowDom)).focus();

        if (saveScroll) {
            me.el.setScrollLeft(scrollLeft);
            me.ignoreScroll = false;
        }
    },

    scrollCellIntoView: function(cell) {
        // Allow cell context object to be passed.
        // TODO: change to .isCellContext check when implement cell context class
        if (cell.row != null && cell.column != null) {
            cell = this.getCellByPosition(cell);
        }
        if (cell) {
            Ext.fly(cell).scrollIntoView(this.el, true);
        }
    },

    /**
     * Scrolls by delta. This affects this individual view ONLY and does not
     * synchronize across views or scrollers.
     * @param {Number} delta
     * @param {String} [dir] Valid values are scrollTop and scrollLeft. Defaults to scrollTop.
     * @private
     */
    scrollByDelta: function(delta, dir) {
        dir = dir || 'scrollTop';
        var elDom = this.el.dom;
        elDom[dir] = (elDom[dir] += delta);
    },

    /**
     * @private
     * Used to test if a row being updated is a basic data row as opposed to containing extra markup
     * provided by a Feature, eg a wrapping row containing extra elements such as group header, group summary,
     * row body etc.
     *
     * If A row being updated *is not* a data row, then the elements within it which are not valid data rows
     * must be copied.
     * @param row
     * @return {Boolean} `true` if the passed element is a basic data row.
     */
    isDataRow: function(row) {
        return Ext.fly(row).hasCls(Ext.baseCSSPrefix + 'grid-data-row');
    },

    syncRowHeights: function(firstRow, secondRow) {
        firstRow = Ext.get(firstRow);
        secondRow = Ext.get(secondRow);
        firstRow.dom.style.height = secondRow.dom.style.height = '';
        var me = this,
            rowTpl = me.rowTpl,
            firstRowHeight = firstRow.dom.offsetHeight,
            secondRowHeight = secondRow.dom.offsetHeight;

        // If the two rows *need* syncing...
        if (firstRowHeight !== secondRowHeight) {

            // loop thru all of rowTpls asking them to sync the two row heights if they know how to.
            while (rowTpl) {
                if (rowTpl.syncRowHeights) {
                    // If any rowTpl in the chain returns false, quit processing
                    if (rowTpl.syncRowHeights(firstRow, secondRow) === false) {
                        break;
                    }
                }
                rowTpl = rowTpl.nextTpl;
            }

            // If that did not fix it, see if we have nested data rows, and equalize the data row heights
            firstRowHeight = firstRow.dom.offsetHeight;
            secondRowHeight = secondRow.dom.offsetHeight;
            if (firstRowHeight !== secondRowHeight) {

                // See if the real data row has been nested deeper by a Feature.
                firstRow = firstRow.down('[data-recordId]') || firstRow;
                secondRow = secondRow.down('[data-recordId]') || secondRow;

                // Yes, there's a nested data row on each side. Sync the heights of the two.
                if (firstRow && secondRow) {
                    firstRow.dom.style.height = secondRow.dom.style.height = '';
                    firstRowHeight = firstRow.dom.offsetHeight;
                    secondRowHeight = secondRow.dom.offsetHeight;

                    if (firstRowHeight > secondRowHeight) {
                        firstRow.setHeight(firstRowHeight);
                        secondRow.setHeight(firstRowHeight);
                    } else if (secondRowHeight > firstRowHeight) {
                        firstRow.setHeight(secondRowHeight);
                        secondRow.setHeight(secondRowHeight);
                    }
                }
            }
        }
    },

    onIdChanged: function(store, rec, oldId, newId, oldInternalId){
        var me = this,
            rowDom;

        if (me.viewReady) {
            rowDom = me.getNodeById(oldInternalId);
            if (rowDom) {
                rowDom.setAttribute('data-recordId', rec.internalId);
                rowDom.id = me.getRowId(rec);
            }
        }
    },

    // private
    onUpdate: function(store, record, operation, changedFieldNames) {
        var me = this,
            rowTpl = me.rowTpl,
            index,
            oldRow, oldRowDom, oldDataRow,
            newRowDom,
            newAttrs, attLen, attName, attrIndex,
            overItemCls, beforeOverItemCls,
            focusedItemCls, beforeFocusedItemCls,
            selectedItemCls, beforeSelectedItemCls,
            columns;

        if (me.viewReady) {
            // Table row being updated
            oldRowDom = me.getNodeByRecord(record, false);

            // Row might not be rendered due to buffered rendering or being part of a collapsed group...
            if (oldRowDom) {
                overItemCls = me.overItemCls;
                beforeOverItemCls = me.beforeOverItemCls;
                focusedItemCls = me.focusedItemCls;
                beforeFocusedItemCls = me.beforeFocusedItemCls;
                selectedItemCls = me.selectedItemCls;
                beforeSelectedItemCls = me.beforeSelectedItemCls;

                index = me.indexInStore(record);
                oldRow = Ext.fly(oldRowDom, '_internal');
                newRowDom = me.createRowElement(record, index);
                if (oldRow.hasCls(overItemCls)) {
                    Ext.fly(newRowDom).addCls(overItemCls);
                }
                if (oldRow.hasCls(beforeOverItemCls)) {
                    Ext.fly(newRowDom).addCls(beforeOverItemCls);
                }
                if (oldRow.hasCls(focusedItemCls)) {
                    Ext.fly(newRowDom).addCls(focusedItemCls);
                }
                if (oldRow.hasCls(beforeFocusedItemCls)) {
                    Ext.fly(newRowDom).addCls(beforeFocusedItemCls);
                }
                if (oldRow.hasCls(selectedItemCls)) {
                    Ext.fly(newRowDom).addCls(selectedItemCls);
                }
                if (oldRow.hasCls(beforeSelectedItemCls)) {
                    Ext.fly(newRowDom).addCls(beforeSelectedItemCls);
                }
                columns = me.ownerCt.getVisibleColumnManager().getColumns();

                // Copy new row attributes across. Use IE-specific method if possible.
                // In IE10, there is a problem where the className will not get updated
                // in the view, even though the className on the dom element is correct.
                // See EXTJSIV-9462
                if (Ext.isIE9m && oldRowDom.mergeAttributes) {
                    oldRowDom.mergeAttributes(newRowDom, true);
                } else {
                    newAttrs = newRowDom.attributes;
                    attLen = newAttrs.length;
                    for (attrIndex = 0; attrIndex < attLen; attrIndex++) {
                        attName = newAttrs[attrIndex].name;
                        if (attName !== 'id') {
                            oldRowDom.setAttribute(attName, newAttrs[attrIndex].value);
                        }
                    }
                }

                // If we have columns which may *need* updating (think locked side of lockable grid with all columns unlocked)
                // and the changed record is within our view, then update the view
                if (columns.length && (oldDataRow = me.getNode(oldRowDom, true))) {
                    me.updateColumns(record, oldDataRow, me.getNode(newRowDom, true), columns, changedFieldNames);
                }

                // loop thru all of rowTpls asking them to sync the content they are responsible for if any.
                while (rowTpl) {
                    if (rowTpl.syncContent) {
                        if (rowTpl.syncContent(oldRowDom, newRowDom) === false) {
                            break;
                        }
                    }
                    rowTpl = rowTpl.nextTpl;
                }

                // Since we don't actually replace the row, we need to fire the event with the old row
                // because it's the thing that is still in the DOM
                me.fireEvent('itemupdate', record, index, oldRowDom);
                me.refreshSize();
            }
        }
    },

    updateColumns: function(record, oldRowDom, newRowDom, columns, changedFieldNames) {
        var me = this,
            newAttrs, attLen, attName, attrIndex,
            colCount = columns.length,
            colIndex,
            column,
            oldCell, newCell,
            row,

            // See if our View has an editingPlugin, or if we are a locking twin, see if the top LockingView
            // has an editingPlugin.
            // We do not support one editing plugin on the top lockable and some other on the twinned views.
            editingPlugin = me.editingPlugin || (me.lockingPartner && me.ownerCt.ownerLockable.view.editingPlugin),

            // See if the found editing plugin is active.
            isEditing = editingPlugin && editingPlugin.editing,
            cellSelector = me.getCellSelector();

            // Copy new row attributes across. Use IE-specific method if possible.
            // Must do again at this level because the row DOM passed here may be the nested row in a row wrap.
            if (oldRowDom.mergeAttributes) {
                oldRowDom.mergeAttributes(newRowDom, true);
            } else {
                newAttrs = newRowDom.attributes;
                attLen = newAttrs.length;
                for (attrIndex = 0; attrIndex < attLen; attrIndex++) {
                    attName = newAttrs[attrIndex].name;
                    if (attName !== 'id') {
                        oldRowDom.setAttribute(attName, newAttrs[attrIndex].value);
                    }
                }
            }

        // Replace changed cells in the existing row structure with the new version from the rendered row.
        for (colIndex = 0; colIndex < colCount; colIndex++) {
            column = columns[colIndex];

            // If the field at this column index was changed, or column has a custom renderer
            // (which means value could rely on any other changed field) the update the cell's content.
            if (me.shouldUpdateCell(record, column, changedFieldNames)) {

                // Pluck out cells using the column's unique cell selector.
                // Becuse in a wrapped row, there may be several TD elements.
                cellSelector = me.getCellSelector(column);
                oldCell = Ext.DomQuery.selectNode(cellSelector, oldRowDom);
                newCell = Ext.DomQuery.selectNode(cellSelector, newRowDom);

                // If an editor plugin is active, we carefully replace just the *contents* of the cell.
                if (isEditing) {
                    Ext.fly(oldCell).syncContent(newCell);
                }
                // Otherwise, we simply replace whole TDs with a new version
                else {
                    // Use immediate parentNode when replacing in case the main row was a wrapping row
                    row = oldCell.parentNode;
                    row.insertBefore(newCell, oldCell);
                    row.removeChild(oldCell);
                }
            }
        }
    },

    shouldUpdateCell: function(record, column, changedFieldNames){
        // Though this may not be the most efficient, a renderer could be dependent on any field in the
        // store, so we must always update the cell.
        // If no changeFieldNames array was passed, we have to assume that that information
        // is unknown and update all cells.
        if (column.hasCustomRenderer || !changedFieldNames) {
            return true;
        }

        if (changedFieldNames) {
            var len = changedFieldNames.length,
                i, field;

            for (i = 0; i < len; ++i) {
                field = changedFieldNames[i];
                if (field === column.dataIndex || field === record.idProperty) {
                    return true;
                }
            }
        }
        return false;
    },

    /**
     * Refreshes the grid view. Sets the sort state and focuses the previously focused row.
     */
    refresh: function() {
        var me = this;

        me.callParent(arguments);
        me.headerCt.setSortState();

        // Create horizontal stretcher element if no records in view and there is overflow of the header container.
        // Element will be transient and destroyed by the next refresh.
        if (me.el && !me.all.getCount() && me.headerCt && me.headerCt.tooNarrow) {
            me.el.createChild({
                role: 'presentation',
                style:'position:absolute;height:1px;width:1px;left:' + (me.headerCt.getFullWidth() - 1) + 'px'
            });
        }

        me.refreshSelection();
    },

    refreshSelection: function() {
        var me = this,
            selModel = me.selModel,
            selected, len, i;

        if (selModel.isRowModel) {
            selected = selModel.selected.items;
            len = selected.length;
            for (i = 0; i < len; i++) {
                me.onRowSelect(me.indexOf(me.getNode(selected[i])));
            }
        }
        me.selModel.onLastFocusChanged(null, me.selModel.lastFocused, true);
    },

    processItemEvent: function(record, row, rowIndex, e) {
        // Only process the event if it occurred within a row which maps to a record in the store
        if (this.indexInStore(row) !== -1) {
            var me = this,
                cell = e.getTarget(me.getCellSelector(), row),
                cellIndex,
                map = me.statics().EventMap,
                selModel = me.getSelectionModel(),
                type = e.type,
                features = me.features,
                len = features.length,
                i, result, feature, header;

            if (type == 'keydown' && !cell && selModel.getCurrentPosition) {
                // CellModel, otherwise we can't tell which cell to invoke
                cell = me.getCellByPosition(selModel.getCurrentPosition(), true);
            }

            // cellIndex is an attribute only of TD elements. Other TplFactories must use the data-cellIndex attribute.
            if (cell) {
                if (!cell.parentNode) {
                    // If we have no parentNode, the td has been removed from the DOM, probably via an update,
                    // so just jump out since the target for the event isn't valid
                    return false;
                }
                // We can't use the cellIndex because we don't render hidden columns
                header = me.getHeaderByCell(cell);

                // Find the index of the header in the *full* (including hidden columns) leaf column set.
                // Because In 4.0.0 we rendered hidden cells, and the cellIndex included the hidden ones.
                cellIndex = me.ownerCt.getColumnManager().getHeaderIndex(header);
            } else {
                cellIndex = -1;
            }

            result = me.fireEvent('uievent', type, me, cell, rowIndex, cellIndex, e, record, row);

            if ((result === false || me.callParent(arguments) === false) && selModel.onVetoUIEvent) {
                selModel.onVetoUIEvent(type, me, cell, rowIndex, cellIndex, e, record, row);
                return false;
            }

            for (i = 0; i < len; ++i) {
                feature = features[i];
                // In some features, the first/last row might be wrapped to contain extra info,
                // such as grouping or summary, so we may need to stop the event
                if (feature.wrapsItem) {
                    if (feature.vetoEvent(record, row, rowIndex, e) === false) {
                        // If the feature is vetoing the event, there's a good chance that
                        // it's for some feature action in the wrapped row.
                        me.processSpecialEvent(e);
                        return false;
                    }
                }
            }

            // Don't handle cellmouseenter and cellmouseleave events for now
            if (type == 'mouseover' || type == 'mouseout') {
                return true;
            }

            if(!cell) {
                // if the element whose event is being processed is not an actual cell (for example if using a rowbody
                // feature and the rowbody element's event is being processed) then do not fire any "cell" events
                return true;
            }

            return !(
                // We are adding cell and feature events
                (me['onBeforeCell' + map[type]](cell, cellIndex, record, row, rowIndex, e) === false) ||
                (me.fireEvent('beforecell' + type, me, cell, cellIndex, record, row, rowIndex, e) === false) ||
                (me['onCell' + map[type]](cell, cellIndex, record, row, rowIndex, e) === false) ||
                (me.fireEvent('cell' + type, me, cell, cellIndex, record, row, rowIndex, e) === false)
            );
        } else {
            // If it's not in the store, it could be a feature event, so check here
            this.processSpecialEvent(e);
            return false;
        }
    },

    processSpecialEvent: function(e) {
        var me = this,
            features = me.features,
            ln = features.length,
            type = e.type,
            i, feature, prefix, featureTarget,
            beforeArgs, args,
            panel = me.ownerCt;

        me.callParent(arguments);

        if (type == 'mouseover' || type == 'mouseout') {
            return;
        }

        for (i = 0; i < ln; i++) {
            feature = features[i];
            if (feature.hasFeatureEvent) {
                featureTarget = e.getTarget(feature.eventSelector, me.getTargetEl());
                if (featureTarget) {
                    prefix = feature.eventPrefix;
                    // allows features to implement getFireEventArgs to change the
                    // fireEvent signature
                    beforeArgs = feature.getFireEventArgs('before' + prefix + type, me, featureTarget, e);
                    args = feature.getFireEventArgs(prefix + type, me, featureTarget, e);

                    if (
                        // before view event
                        (me.fireEvent.apply(me, beforeArgs) === false) ||
                        // panel grid event
                        (panel.fireEvent.apply(panel, beforeArgs) === false) ||
                        // view event
                        (me.fireEvent.apply(me, args) === false) ||
                        // panel event
                        (panel.fireEvent.apply(panel, args) === false)
                    ) {
                        return false;
                    }
                }
            }
        }
        return true;
    },

    onCellMouseDown: Ext.emptyFn,
    onCellMouseUp: Ext.emptyFn,
    onCellClick: Ext.emptyFn,
    onCellDblClick: Ext.emptyFn,
    onCellContextMenu: Ext.emptyFn,
    onCellKeyDown: Ext.emptyFn,
    onBeforeCellMouseDown: Ext.emptyFn,
    onBeforeCellMouseUp: Ext.emptyFn,
    onBeforeCellClick: Ext.emptyFn,
    onBeforeCellDblClick: Ext.emptyFn,
    onBeforeCellContextMenu: Ext.emptyFn,
    onBeforeCellKeyDown: Ext.emptyFn,

    /**
     * Expands a particular header to fit the max content width.
     * @deprecated Use {@link #autoSizeColumn} instead.
     */
    expandToFit: function(header) {
        this.autoSizeColumn(header);
    },

    /**
     * Sizes the passed header to fit the max content width.
     * *Note that group columns shrinkwrap around the size of leaf columns. Auto sizing a group column
     * autosizes descendant leaf columns.*
     * @param {Ext.grid.column.Column/Number} header The header (or index of header) to auto size.
     */
    autoSizeColumn: function(header) {
        if (Ext.isNumber(header)) {
            header = this.getGridColumns[header];
        }
        if (header) {
            if (header.isGroupHeader) {
                header.autoSize();
                return;
            }
            delete header.flex;
            header.setWidth(this.getMaxContentWidth(header));
        }
    },

    /**
     * Returns the max contentWidth of the header's text and all cells
     * in the grid under this header.
     * @private
     */
    getMaxContentWidth: function(header) {
        var me = this,
            cells = me.el.query(header.getCellInnerSelector()),
            originalWidth = header.getWidth(),
            i = 0,
            ln = cells.length,
            columnSizer = me.body.select(me.getColumnSizerSelector(header)),
            max = Math.max,
            widthAdjust = 0,
            maxWidth;

        if (ln > 0) {
            if (Ext.supports.ScrollWidthInlinePaddingBug) {
                widthAdjust += me.getCellPaddingAfter(cells[0]);
            }
            if (me.columnLines) {
                widthAdjust += Ext.fly(cells[0].parentNode).getBorderWidth('lr');
            }
        }

        // Set column width to 1px so we can detect the content width by measuring scrollWidth
        columnSizer.setWidth(1);

        // We are about to measure the offsetWidth of the textEl to determine how much
        // space the text occupies, but it will not report the correct width if the titleEl
        // has text-overflow:ellipsis.  Set text-overflow to 'clip' before proceeding to
        // ensure we get the correct measurement.
        header.titleEl.setStyle('text-overflow', 'clip');

        // Allow for padding round text of header
        maxWidth = header.textEl.dom.offsetWidth + header.titleEl.getPadding('lr');

        // revert to using text-overflow defined by the stylesheet
        header.titleEl.setStyle('text-overflow', '');

        for (; i < ln; i++) {
            maxWidth = max(maxWidth, cells[i].scrollWidth);
        }

        // in some browsers, the "after" padding is not accounted for in the scrollWidth
        maxWidth += widthAdjust;

        // 40 is the minimum column width.  TODO: should this be configurable?
        maxWidth = max(maxWidth, 40);

        // Set column width back to original width
        columnSizer.setWidth(originalWidth);

        return maxWidth;
    },

    getPositionByEvent: function(e) {
        var me       = this,
            cellNode = e.getTarget(me.cellSelector),
            rowNode  = e.getTarget(me.itemSelector),
            record   = me.getRecord(rowNode),
            header   = me.getHeaderByCell(cellNode);

        return me.getPosition(record, header);
    },

    getHeaderByCell: function(cell) {
        if (cell) {
            var match = cell.className.match(this.cellRe);
            if (match && match[1]) {
                return this.ownerCt.getVisibleColumnManager().getHeaderById(match[1]);
            }
        }
        return false;
    },

    /**
     * @param {Object} position The current row and column: an object containing the following properties:
     *
     * - row - The row index
     * - column - The column index
     *
     * @param {String} direction 'up', 'down', 'right' and 'left'
     * @param {Ext.EventObject} e event
     * @param {Boolean} preventWrap Set to true to prevent wrap around to the next or previous row.
     * @param {Function} verifierFn A function to verify the validity of the calculated position.
     * When using this function, you must return true to allow the newPosition to be returned.
     * @param {Object} scope Scope to run the verifierFn in
     * @returns {Ext.grid.CellContext} An object encapsulating the unique cell position.
     *
     * @private
     */
    walkCells: function(pos, direction, e, preventWrap, verifierFn, scope) {

        // Caller (probably CellModel) had no current position. This can happen
        // if the main el is focused and any navigation key is presssed.
        if (!pos) {
            return false;
        }

        var me           = this,
            row          = pos.row,
            column       = pos.column,
            rowCount     = me.dataSource.getCount(),
            allCols      = me.ownerCt.getColumnManager(),
            visible      = me.ownerCt.getVisibleColumnManager(),
            firstIndex   = allCols.getHeaderIndex(visible.getFirst()),
            lastIndex    = allCols.getHeaderIndex(visible.getLast()),
            newRow       = row,
            newColumn    = column,
            activeHeader = allCols.getHeaderAtIndex(column);

        // no active header or its currently hidden
        if (!activeHeader || activeHeader.hidden || !rowCount) {
            return false;
        }

        e = e || {};
        direction = direction.toLowerCase();
        switch (direction) {
            case 'right':
                // has the potential to wrap if its last
                if (column === lastIndex) {
                    // if bottom row and last column, deny right
                    if (preventWrap || row === rowCount - 1) {
                        return false;
                    }
                    if (!e.ctrlKey) {
                        // otherwise wrap to nextRow and firstCol
                        newRow = me.walkRows(row, 1);
                        if (newRow !== row) {
                            newColumn = firstIndex;
                        }
                    }
                // go right
                } else {
                    if (!e.ctrlKey) {
                        newColumn = allCols.getHeaderIndex(visible.getNextSibling(activeHeader));
                    } else {
                        newColumn = lastIndex;
                    }
                }
                break;

            case 'left':
                // has the potential to wrap
                if (column === firstIndex) {
                    // if top row and first column, deny left
                    if (preventWrap || row === 0) {
                        return false;
                    }
                    if (!e.ctrlKey) {
                        // otherwise wrap to prevRow and lastIndex
                        newRow = me.walkRows(row, -1);
                        if (newRow !== row) {
                            newColumn = lastIndex;
                        }
                    }
                // go left
                } else {
                    if (!e.ctrlKey) {
                        newColumn = allCols.getHeaderIndex(visible.getPreviousSibling(activeHeader));
                    } else {
                        newColumn = firstIndex;
                    }
                }
                break;

            case 'up':
                // if top row, deny up
                if (row === 0) {
                    return false;
                // go up
                } else {
                    if (!e.ctrlKey) {
                        newRow = me.walkRows(row, -1);
                    } else {
                        // Go to first row by walking down from row -1
                        newRow = me.walkRows(-1, 1);
                    }
                }
                break;

            case 'down':
                // if bottom row, deny down
                if (row === rowCount - 1) {
                    return false;
                // go down
                } else {
                    if (!e.ctrlKey) {
                        newRow = me.walkRows(row, 1);
                    } else {
                        // Go to first row by walking up from beyond the last row
                        newRow = me.walkRows(rowCount, -1);
                    }
                }
                break;
        }

        if (verifierFn && verifierFn.call(scope || me, {row: newRow, column: newColumn}) !== true) {
            return false;
        }

        newColumn = allCols.getHeaderAtIndex(newColumn);
        return new Ext.grid.CellContext(me).setPosition(newRow, newColumn);
    },

    /**
     * Increments the passed row index by the passed increment which may be +ve or -ve
     *
     * Skips hidden rows.
     *
     * If no row is visible in the specified direction, returns the input row index unchanged.
     * @param {Number} startRow The zero-based row index to start from.
     * @param {Number} distance The distance to move the row by. May be +ve or -ve.
     */
    walkRows: function(startRow, distance) {
        // Note that we use the **dataSource** here because row indices mean view row indices
        // so records in collapsed groups must be omitted.
        var me = this,
            moved = 0,
            lastValid = startRow,
            node,
            last = (me.dataSource.buffered ? me.dataSource.getTotalCount() : me.dataSource.getCount()) - 1,
            limit = (distance < 0) ? 0 : last,
            increment = limit ? 1 : -1,
            result = startRow;

        do {
            // Walked off the end: return the last encountered valid row
            if (limit ? result >= limit : result <= 0) {
                return lastValid || limit;
            }

            // Move the result pointer on by one position. We have to count intervening VISIBLE nodes
            result += increment;

            // Stepped onto VISIBLE record: Increment the moved count.
            // We must not count stepping onto a non-rendered record as a move.
            if ((node = Ext.fly(me.getNode(result, true))) && node.isVisible(true)) {
                moved += increment;
                lastValid = result;
            }
        } while (moved !== distance);
        return result;
    },

    /**
     * Navigates from the passed record by the passed increment which may be +ve or -ve
     *
     * Skips hidden records.
     *
     * If no record is visible in the specified direction, returns the starting record index unchanged.
     * @param {Ext.data.Model} startRec The Record to start from.
     * @param {Number} distance The distance to move from the record. May be +ve or -ve.
     */
    walkRecs: function(startRec, distance) {
        // Note that we use the **store** to access the records by index because the dataSource omits records in collapsed groups.
        // This is used by selection models which use the **store**
        var me = this,
            moved = 0,
            lastValid = startRec,
            node,
            last = (me.store.buffered ? me.store.getTotalCount() : me.store.getCount()) - 1,
            limit = (distance < 0) ? 0 : last,
            increment = limit ? 1 : -1,
            testIndex = me.store.indexOf(startRec),
            rec;

        do {
            // Walked off the end: return the last encountered valid record
            if (limit ? testIndex >= limit : testIndex <= 0) {
                return lastValid;
            }

            // Move the result pointer on by one position. We have to count intervening VISIBLE nodes
            testIndex += increment;

            // Stepped onto VISIBLE record: Increment the moved count.
            // We must not count stepping onto a non-rendered record as a move.
            rec = me.store.getAt(testIndex);
            if ((node = Ext.fly(me.getNodeByRecord(rec, true))) && node.isVisible(true)) {
                moved += increment;
                lastValid = rec;
            }
        } while (moved !== distance);
        return lastValid;
    },

    getFirstVisibleRowIndex: function() {
        var me = this,
            count = (me.dataSource.buffered ? me.dataSource.getTotalCount() : me.dataSource.getCount()),
            result = me.indexOf(me.all.first()) - 1;

        do {
            result += 1;
            if (result === count) {
                return;
            }
        } while (!Ext.fly(me.getNode(result, true)).isVisible(true));
        return result;
    },

    getLastVisibleRowIndex: function() {
        var me = this,
            result = me.indexOf(me.all.last());

        do {
            result -= 1;
            if (result === -1) {
                return;
            }
        } while (!Ext.fly(me.getNode(result, true)).isVisible(true));
        return result;
    },

    getHeaderCt: function() {
        return this.headerCt;
    },

    getPosition: function(record, header) {
        return new Ext.grid.CellContext(this).setPosition(record, header);
    },

    beforeDestroy: function() {
        var me = this;

        if (me.rendered) {
            me.el.removeAllListeners();
        }
        me.callParent(arguments);
    },

    onDestroy: function() {
        var me = this,
            features = me.featuresMC,
            len,
            i;

        if (features) {
            for (i = 0, len = features.getCount(); i < len; ++i) {
                features.getAt(i).destroy();
            }
        }
        me.featuresMC = null;
        this.callParent(arguments);
    },

    // after adding a row stripe rows from then on
    onAdd: function(ds, records, index) {
        var me = this,
            selModel = me.selModel,
            nextIndex, isNextRowSelected, isNextRowFocused;

        me.callParent(arguments);
        me.doStripeRows(index);

        if (me.rendered && selModel.isRowModel && !records[0].isCollapsedPlaceholder) {
            nextIndex = index + records.length;
            isNextRowSelected = selModel.isRowSelected(nextIndex);
            isNextRowFocused = me.indexOf(selModel.lastFocused) === (nextIndex);
            if (isNextRowSelected || isNextRowFocused) {
                // calling onRowDeselect ensures before focus/selected border gets removed from
                // previous row if the row previously at this index was selected or focused.
                me.onRowDeselect(index);
            }
            if (isNextRowSelected) {
                me.onRowSelect(nextIndex);
            }
            if (selModel.isRowSelected(index)) {
                me.onRowSelect(index);
            }
        }
        me.selModel.onLastFocusChanged(null, me.selModel.lastFocused, true);
    },

    // after removing a row stripe rows from then on
    onRemove: function(ds, records, indexes) {
        var me = this,
            index,
            selModel = me.selModel,
            len = indexes.length,
            i = 0,
            delta = 0,
            record;

        // Ensure "beforeSelected" classes are maintained correctly.
        // Some of the removed rows my have been carrying the beforeSelected class.
        me.callParent(arguments);
        me.doStripeRows(indexes[0]);

        if (me.rendered && selModel.isRowModel && !records[0].isCollapsedPlaceholder) {
            for (; i < len; i++, delta++) {
                index = indexes[i] - delta;
                record = me.store.getAt(index);
                me.onRowDeselect(index);
                if (selModel.isRowSelected(record) && me.getNode(record)) {
                    me.onRowSelect(index);
                }
            }
        }
        selModel.onLastFocusChanged(null, selModel.lastFocused, true);
    },

    /**
     * Stripes rows from a particular row index.
     * @param {Number} startRow
     * @param {Number} [endRow] argument specifying the last row to process.
     * By default process up to the last row.
     * @private
     */
    doStripeRows: function(startRow, endRow) {
        var me = this,
            rows,
            rowsLn,
            i,
            row;

        // ensure stripeRows configuration is turned on
        if (me.rendered && me.stripeRows) {
            rows = me.getNodes(startRow, endRow);

            for (i = 0, rowsLn = rows.length; i < rowsLn; i++) {
                row = rows[i];
                // Remove prior applied row classes.
                row.className = row.className.replace(me.rowClsRe, ' ');
                startRow++;
                // Every odd row will get an additional cls
                if (startRow % 2 === 0) {
                    row.className += (' ' + me.altRowCls);
                }
            }
        }
    },

    repaintRow: function(rowIdx) {
        var node = this.getNode(rowIdx),
            tds,
            i;

        if (node) {
            tds = node.childNodes;
            i = tds.length;
            while (i--) {
                tds[i].className = tds[i].className;
            }
        }
    },

    // private
    // returns the table that gains a top border when the first grid row is focused, selected,
    // or hovered.  Usually the main grid table but can be a sub-table, if a grouping
    // feature is being used.
    getRowStyleTableEl: function(item /* view item or row index */) {
       var me = this;

        if (!item.tagName) {
            item = this.getNode(item);
        }
        return (me.hasActiveGrouping() ? Ext.fly(item) : this.el).down('table.' + Ext.baseCSSPrefix + 'grid-table');
    },

    // private
    // adds or removes a css class to or from the table that gains a top border when the
    // first grid row is focused, selected, or hovered.  Usually the main grid table but
    // can be a sub-table, if a grouping feature is being used.
    toggleRowTableCls: function(item /* view item or row index */, cls, enabled) {
       var me = this,
           table, root;

        if (!item.tagName) {
            item = this.getNode(item);
        }

        root = me.isGrouping ? Ext.fly(item) : this.el;

        if (root) {
            table = root.down('table.' + Ext.baseCSSPrefix + 'grid-table');
        }

        if (table) {
            table[enabled ? 'addCls' : 'removeCls'](cls);
        }
    },

    // private
    // returns true if the row should be treated as the first row stylistically.  Typically
    // only returns true for the first row in a grid.  Returns true for the first row
    // in each group of a grouped grid.
    isRowStyleFirst: function(item /* view item or row index */) {
        var me = this,
            index;

        // An item not in the view
        if (item === -1) {
            return false;
        }

        if (!item.tagName) {
            index = item;
            item = this.getNode(item);
        } else {
            index = me.indexOf(item);
        }

        return (!index || me.hasActiveGrouping() && item && Ext.fly(item).hasCls(Ext.baseCSSPrefix + 'grid-group-row'));
    },

    hasActiveGrouping: function(){
        return this.isGrouping && this.store.isGrouped();
    },

    getCellPaddingAfter: function(cell) {
        return Ext.fly(cell).getPadding('r');
    },

    hasVerticalScroll: function(){
        var me = this,
            first;

        if (me.ownerCt.isLocked || !me.scrollFlags.y) {
            return false;
        }

        first = me.el.down('table');
        if (!first) {
            return false;
        }

        return this.getHeight() < first.getHeight(); 
    }

});

/**
 * The grid View class provides extra {@link Ext.grid.Panel} specific functionality to the
 * {@link Ext.view.Table}. In general, this class is not instanced directly, instead a viewConfig
 * option is passed to the grid:
 *
 *     Ext.create('Ext.grid.Panel', {
 *         // other options
 *         viewConfig: {
 *             stripeRows: false
 *         }
 *     });
 *
 * ## Drag Drop
 *
 * Drag and drop functionality can be achieved in the grid by attaching a {@link Ext.grid.plugin.DragDrop} plugin
 * when creating the view.
 *
 *     Ext.create('Ext.grid.Panel', {
 *         // other options
 *         viewConfig: {
 *             plugins: {
 *                 ddGroup: 'people-group',
 *                 ptype: 'gridviewdragdrop',
 *                 enableDrop: false
 *             }
 *         }
 *     });
 */
Ext.define('Ext.grid.View', {
    extend:  Ext.view.Table ,
    alias: 'widget.gridview',

    /**
     * @cfg {Boolean}
     * True to stripe the rows.
     *
     * This causes the CSS class **`x-grid-row-alt`** to be added to alternate rows of the grid. A default CSS rule is
     * provided which sets a background color, but you can override this with a rule which either overrides the
     * **background-color** style using the `!important` modifier, or which uses a CSS selector of higher specificity.
     */
    stripeRows: true,

    autoScroll: true
});

/**
 * @author Aaron Conran
 * @docauthor Ed Spencer
 *
 * Grids are an excellent way of showing large amounts of tabular data on the client side. Essentially a supercharged
 * `<table>`, GridPanel makes it easy to fetch, sort and filter large amounts of data.
 *
 * Grids are composed of two main pieces - a {@link Ext.data.Store Store} full of data and a set of columns to render.
 *
 * ## Basic GridPanel
 *
 *     @example
 *     Ext.create('Ext.data.Store', {
 *         storeId:'simpsonsStore',
 *         fields:['name', 'email', 'phone'],
 *         data:{'items':[
 *             { 'name': 'Lisa',  "email":"lisa@simpsons.com",  "phone":"555-111-1224"  },
 *             { 'name': 'Bart',  "email":"bart@simpsons.com",  "phone":"555-222-1234" },
 *             { 'name': 'Homer', "email":"home@simpsons.com",  "phone":"555-222-1244"  },
 *             { 'name': 'Marge', "email":"marge@simpsons.com", "phone":"555-222-1254"  }
 *         ]},
 *         proxy: {
 *             type: 'memory',
 *             reader: {
 *                 type: 'json',
 *                 root: 'items'
 *             }
 *         }
 *     });
 *
 *     Ext.create('Ext.grid.Panel', {
 *         title: 'Simpsons',
 *         store: Ext.data.StoreManager.lookup('simpsonsStore'),
 *         columns: [
 *             { text: 'Name',  dataIndex: 'name' },
 *             { text: 'Email', dataIndex: 'email', flex: 1 },
 *             { text: 'Phone', dataIndex: 'phone' }
 *         ],
 *         height: 200,
 *         width: 400,
 *         renderTo: Ext.getBody()
 *     });
 *
 * The code above produces a simple grid with three columns. We specified a Store which will load JSON data inline.
 * In most apps we would be placing the grid inside another container and wouldn't need to use the
 * {@link #height}, {@link #width} and {@link #renderTo} configurations but they are included here to make it easy to get
 * up and running.
 *
 * The grid we created above will contain a header bar with a title ('Simpsons'), a row of column headers directly underneath
 * and finally the grid rows under the headers.
 *
 * ## Configuring columns
 *
 * By default, each column is sortable and will toggle between ASC and DESC sorting when you click on its header. Each
 * column header is also reorderable by default, and each gains a drop-down menu with options to hide and show columns.
 * It's easy to configure each column - here we use the same example as above and just modify the columns config:
 *
 *     columns: [
 *         {
 *             text: 'Name',
 *             dataIndex: 'name',
 *             sortable: false,
 *             hideable: false,
 *             flex: 1
 *         },
 *         {
 *             text: 'Email',
 *             dataIndex: 'email',
 *             hidden: true
 *         },
 *         {
 *             text: 'Phone',
 *             dataIndex: 'phone',
 *             width: 100
 *         }
 *     ]
 *
 * We turned off sorting and hiding on the 'Name' column so clicking its header now has no effect. We also made the Email
 * column hidden by default (it can be shown again by using the menu on any other column). We also set the Phone column to
 * a fixed with of 100px and flexed the Name column, which means it takes up all remaining width after the other columns
 * have been accounted for. See the {@link Ext.grid.column.Column column docs} for more details.
 *
 * ## Renderers
 *
 * As well as customizing columns, it's easy to alter the rendering of individual cells using renderers. A renderer is
 * tied to a particular column and is passed the value that would be rendered into each cell in that column. For example,
 * we could define a renderer function for the email column to turn each email address into a mailto link:
 *
 *     columns: [
 *         {
 *             text: 'Email',
 *             dataIndex: 'email',
 *             renderer: function(value) {
 *                 return Ext.String.format('<a href="mailto:{0}">{1}</a>', value, value);
 *             }
 *         }
 *     ]
 *
 * See the {@link Ext.grid.column.Column column docs} for more information on renderers.
 *
 * ## Selection Models
 *
 * Sometimes all you want is to render data onto the screen for viewing, but usually it's necessary to interact with or
 * update that data. Grids use a concept called a Selection Model, which is simply a mechanism for selecting some part of
 * the data in the grid. The two main types of Selection Model are RowSelectionModel, where entire rows are selected, and
 * CellSelectionModel, where individual cells are selected.
 *
 * Grids use a Row Selection Model by default, but this is easy to customise like so:
 *
 *     Ext.create('Ext.grid.Panel', {
 *         selType: 'cellmodel',
 *         store: ...
 *     });
 *
 * Specifying the `cellmodel` changes a couple of things. Firstly, clicking on a cell now
 * selects just that cell (using a {@link Ext.selection.RowModel rowmodel} will select the entire row), and secondly the
 * keyboard navigation will walk from cell to cell instead of row to row. Cell-based selection models are usually used in
 * conjunction with editing.
 *
 * ## Sorting & Filtering
 *
 * Every grid is attached to a {@link Ext.data.Store Store}, which provides multi-sort and filtering capabilities. It's
 * easy to set up a grid to be sorted from the start:
 *
 *     var myGrid = Ext.create('Ext.grid.Panel', {
 *         store: {
 *             fields: ['name', 'email', 'phone'],
 *             sorters: ['name', 'phone']
 *         },
 *         columns: [
 *             { text: 'Name',  dataIndex: 'name' },
 *             { text: 'Email', dataIndex: 'email' }
 *         ]
 *     });
 *
 * Sorting at run time is easily accomplished by simply clicking each column header. If you need to perform sorting on
 * more than one field at run time it's easy to do so by adding new sorters to the store:
 *
 *     myGrid.store.sort([
 *         { property: 'name',  direction: 'ASC' },
 *         { property: 'email', direction: 'DESC' }
 *     ]);
 *
 * See {@link Ext.data.Store} for examples of filtering.
 *
 * ## State saving
 * 
 * When configured {@link #stateful}, grids save their column state (order and width) encapsulated within the default
 * Panel state of changed width and height and collapsed/expanded state.
 *
 * Each {@link #columns column} of the grid may be configured with a {@link Ext.grid.column.Column#stateId stateId} which
 * identifies that column locally within the grid.
 *
 * ## Plugins and Features
 *
 * Grid supports addition of extra functionality through features and plugins:
 *
 * - {@link Ext.grid.plugin.CellEditing CellEditing} - editing grid contents one cell at a time.
 *
 * - {@link Ext.grid.plugin.RowEditing RowEditing} - editing grid contents an entire row at a time.
 *
 * - {@link Ext.grid.plugin.DragDrop DragDrop} - drag-drop reordering of grid rows.
 *
 * - {@link Ext.toolbar.Paging Paging toolbar} - paging through large sets of data.
 *
 * - {@link Ext.grid.plugin.BufferedRenderer Infinite scrolling} - another way to handle large sets of data.
 *
 * - {@link Ext.grid.RowNumberer RowNumberer} - automatically numbered rows.
 *
 * - {@link Ext.grid.feature.Grouping Grouping} - grouping together rows having the same value in a particular field.
 *
 * - {@link Ext.grid.feature.Summary Summary} - a summary row at the bottom of a grid.
 *
 * - {@link Ext.grid.feature.GroupingSummary GroupingSummary} - a summary row at the bottom of each group.
 */
Ext.define('Ext.grid.Panel', {
    extend:  Ext.panel.Table ,
                                
    alias: ['widget.gridpanel', 'widget.grid'],
    alternateClassName: ['Ext.list.ListView', 'Ext.ListView', 'Ext.grid.GridPanel'],
    viewType: 'gridview',

    lockable: false,

    /**
     * @cfg {Boolean} rowLines False to remove row line styling
     */
    rowLines: true

    // Columns config is required in Grid
    /**
     * @cfg {Ext.grid.column.Column[]/Object} columns (required)
     * @inheritdoc
     */

    /**
     * @event beforereconfigure
     * Fires before a reconfigure to enable modification of incoming Store and columns.
     * @param {Ext.grid.Panel} this
     * @param {Ext.data.Store} store The store that was passed to the {@link #method-reconfigure} method
     * @param {Object[]} columns The column configs that were passed to the {@link #method-reconfigure} method
     * @param {Ext.data.Store} oldStore The store that will be replaced
     * @param {Ext.grid.column.Column[]} The column headers that will be replaced.
     */

    /**
     * @event reconfigure
     * Fires after a reconfigure.
     * @param {Ext.grid.Panel} this
     * @param {Ext.data.Store} store The store that was passed to the {@link #method-reconfigure} method
     * @param {Object[]} columns The column configs that were passed to the {@link #method-reconfigure} method
     * @param {Ext.data.Store} oldStore The store that was replaced
     * @param {Ext.grid.column.Column[]} The column headers that were replaced.
     */

    /**
     * @method reconfigure
     * Reconfigures the grid with a new store/columns. Either the store or the columns can be omitted if you don't wish
     * to change them.
     * @param {Ext.data.Store} store (Optional) The new store.
     * @param {Object[]} columns (Optional) An array of column configs
     */
});

/**
 * @private
 * A set of overrides required by the presence of the BufferedRenderer plugin.
 * 
 * These overrides of Ext.view.Table take into account the affect of a buffered renderer and
 * divert execution from the default course where necessary.
 */
Ext.define('Ext.grid.plugin.BufferedRendererTableView', {
    override: 'Ext.view.Table',

    // Listener function for the Store's add event
    onAdd: function(store, records, index) {
        var me = this,
            bufferedRenderer = me.bufferedRenderer,
            rows = me.all;

        // The newly added records will put us over the buffered view size, so we cannot just add as normal.
        if (me.rendered && bufferedRenderer && (rows.getCount() + records.length) > bufferedRenderer.viewSize) {

            // Index puts the new row(s) in the visible area, then we have to refresh the view
            if (index < rows.startIndex + bufferedRenderer.viewSize && (index + records.length) > rows.startIndex) {
                me.refreshView();
            }
            // New rows outside of visible area, just ensure that the scroll range is updated
            else {
                bufferedRenderer.stretchView(me, bufferedRenderer.getScrollHeight());
            }
        }
        // No BufferedRenderer present
        // or
        // View has not yet reached the viewSize: we can add as normal.
        else {
            me.callParent([store, records, index]);
        }
    },

    onRemove: function(store, records, indices) {
        var me = this,
            bufferedRenderer = me.bufferedRenderer,
            storeSize, all, startIndex;

        // Ensure all records are removed from the view
        me.callParent([store, records, indices]);

        // If there's a BufferedRenderer, the view must refresh to keep the view correct.
        // Removing *may* have removed all of the rendered rows, leaving whitespace below the group header, 
        // so the buffered renderer will be needed to keep the buffer rendered zone valid - to pull records up from
        // below the removed zone into visibility.
        if (me.rendered && bufferedRenderer) {
            storeSize = me.dataSource.getCount();
            all = me.all;
            // If that remove left a gap below the rendered range, ask the buffered renderer to render its
            // current view size. This will do the minimum work required, and *append* rows to the table only
            // if necessary
            if (storeSize > all.getCount()) {
                startIndex = all.startIndex;
                bufferedRenderer.renderRange(startIndex, Math.min(startIndex + bufferedRenderer.viewSize, storeSize) - 1);
            }
            // No overflow, still we have to ensure the scroll range is updated
            else {
                bufferedRenderer.stretchView(me, bufferedRenderer.getScrollHeight());
            }
        }
    },

    // When there's a buffered renderer present, store refresh events cause TableViews to go to scrollTop:0
    onDataRefresh: function() {
        var me = this;

        if (me.bufferedRenderer) {
            // Clear NodeCache. Do NOT remove nodes from DOM - that would blur the view, and then refresh will not refocus after the refresh.
            me.all.clear();
            me.bufferedRenderer.onStoreClear();
        }
        me.callParent();
    }
});

/**
 * @private
 * Private Container class used by the {@link Ext.grid.RowEditor} to hold its buttons.
 */
Ext.define('Ext.grid.RowEditorButtons', {
    extend:  Ext.container.Container ,
    alias: 'widget.roweditorbuttons',

    frame: true,
    shrinkWrap: true,
    position: 'bottom',

    constructor: function(config) {
        var me = this,
            rowEditor = config.rowEditor,
            cssPrefix = Ext.baseCSSPrefix,
            plugin = rowEditor.editingPlugin;

        config = Ext.apply({
            baseCls: cssPrefix + 'grid-row-editor-buttons',
            defaults: {
                xtype: 'button',
                ui: rowEditor.buttonUI,
                scope: plugin,
                flex: 1,
                minWidth: Ext.panel.Panel.prototype.minButtonWidth
            },
            items: [{
                cls: cssPrefix + 'row-editor-update-button',
                itemId: 'update',
                handler: plugin.completeEdit,
                text: rowEditor.saveBtnText,
                disabled: rowEditor.updateButtonDisabled
            }, {
                cls: cssPrefix + 'row-editor-cancel-button',
                handler: plugin.cancelEdit,
                text: rowEditor.cancelBtnText
            }]
        }, config);

        me.callParent([config]);

        me.addClsWithUI(me.position);
    },

    setButtonPosition: function(position) {
        var me = this,
            rowEditor = this.rowEditor,
            rowEditorHeight = rowEditor.getHeight(),
            rowEditorBody = rowEditor.body,
            bottom = '',
            top = '';

        me.removeClsWithUI(me.position);
        me.position = position;
        me.addClsWithUI(position);
        // we tried setting the top/bottom value in the stylesheet based on form field
        // height + row editor padding, but that approach does not work when there are
        // larger things inside the editor, e.g. textarea, so we have to measure
        // the row editor height and position the buttons accordingly (see EXTJSIV-9914).
        if (position === 'top') {
            bottom = (rowEditorHeight - rowEditorBody.getBorderWidth('t')) + 'px';
        } else {
            top = (rowEditorHeight - rowEditorBody.getBorderWidth('b')) + 'px';
        }

        me.el.setStyle({
            top: top,
            bottom: bottom
        });
    },

    getFramingInfoCls: function(){
        return this.baseCls + '-' + this.ui + '-' + this.position;
    },

    getFrameInfo: function() {
        var frameInfo = this.callParent();

        // Trick Renderable into rendering the top framing elements, even though they
        // are not needed in the default "bottom" position.  This allows us to flip the
        // buttons into "top" position without re-rendering.
        frameInfo.top = true;

        return frameInfo;
    }
});

// Currently has the following issues:
// - Does not handle postEditValue
// - Fields without editors need to sync with their values in Store
// - starting to edit another record while already editing and dirty should probably prevent it
// - aggregating validation messages
// - tabIndex is not managed bc we leave elements in dom, and simply move via positioning
// - layout issues when changing sizes/width while hidden (layout bug)

/**
 * Internal utility class used to provide row editing functionality. For developers, they should use
 * the RowEditing plugin to use this functionality with a grid.
 *
 * @private
 */
Ext.define('Ext.grid.RowEditor', {
    extend:  Ext.form.Panel ,
    alias: 'widget.roweditor',
               
                          
                          
                                   
      

    //<locale>
    saveBtnText  : 'Update',
    //</locale>
    //<locale>
    cancelBtnText: 'Cancel',
    //</locale>
    //<locale>
    errorsText: 'Errors',
    //</locale>
    //<locale>
    dirtyText: 'You need to commit or cancel your changes',
    //</locale>

    lastScrollLeft: 0,
    lastScrollTop: 0,

    border: false,

    errorCls: Ext.baseCSSPrefix + 'grid-row-editor-errors-item',
    buttonUI: 'default',

    // Change the hideMode to offsets so that we get accurate measurements when
    // the roweditor is hidden for laying out things like a TriggerField.
    hideMode: 'offsets',

    initComponent: function() {
        var me = this,
            grid = me.editingPlugin.grid,
            Container = Ext.container.Container,
            form;

        me.cls = Ext.baseCSSPrefix + 'grid-editor ' + Ext.baseCSSPrefix + 'grid-row-editor';

        me.layout = {
            type: 'hbox',
            align: 'middle'
        };

        me.lockable = grid.lockable;

        // Create field containing structure for when editing a lockable grid.
        if (me.lockable) {
            me.items = [
                // Locked columns container shrinkwraps the fields
                me.lockedColumnContainer = new Container({
                    id: grid.id + '-locked-editor-cells',
                    layout: {
                        type: 'hbox',
                        align: 'middle'
                    },
                    // Locked grid has a border, we must be exactly the same width
                    margin: '0 1 0 0'
                }),

                // Normal columns container flexes the remaining RowEditor width
                me.normalColumnContainer = new Container({
                    flex: 1,
                    id: grid.id + '-normal-editor-cells',
                    layout: {
                        type: 'hbox',
                        align: 'middle'
                    }
                })
            ];
        } else {
            me.lockedColumnContainer = me.normalColumnContainer = me;
        }

        me.callParent(arguments);

        if (me.fields) {
            me.addFieldsForColumn(me.fields, true);
            me.insertColumnEditor(me.fields);
            delete me.fields;
        }

        me.mon(me.hierarchyEventSource, {
            scope: me,
            show: me.repositionIfVisible
        });
        
        form = me.getForm();
        form.trackResetOnLoad = true;
        form.on('validitychange', me.onValidityChange, me);
    },

    //
    // Grid listener added when this is rendered.
    // Keep our containing element sized correctly
    //
    onGridResize: function() {
        var me = this,
            clientWidth = me.getClientWidth(),
            grid = me.editingPlugin.grid,
            gridBody = grid.body,
            btns = me.getFloatingButtons();

        me.setLocalX(gridBody.getOffsetsTo(grid)[0] + gridBody.getBorderWidth('l') - grid.el.getBorderWidth('l'));
        
        me.setWidth(clientWidth);
        btns.setLocalX((clientWidth - btns.getWidth()) / 2);
    },
    
    syncAllFieldWidths: function() {
        var me = this;
        // In a locked grid, a RowEditor uses 2 inner containers, so need to use CQ to retrieve
        // configured editors which were stamped with the isEditorComponent property in Editing.createColumnField
        Ext.Array.each(me.query('[isEditorComponent]'), function(editorComponent) {
            if (editorComponent.column.isVisible()) {
                me.onColumnShow(editorComponent.column);
            }
        }, me);    
    },

    syncFieldWidth: function(column) {
        var field = column.getEditor(),
            width;
        field._marginWidth = (field._marginWidth || field.el.getMargin('lr'));
        width = column.getWidth() - field._marginWidth;
        field.setWidth(width);
        if (field.xtype === 'displayfield') {
            // displayfield must have the width set on the inputEl for ellipsis to work
            field.inputWidth = width;
        }
    },

    onValidityChange: function(form, valid) {
        var me = this;
            
        if (me.errorSummary && me.isVisible()) {
            me[valid ? 'hideToolTip' : 'showToolTip']();
        }
        me.updateButton(valid);
        me.isValid = valid;
    },

    updateButton: function(valid){
        var buttons = this.floatingButtons; 
        if (buttons) {
            buttons.child('#update').setDisabled(!valid);
        } else {
            // set flag so we can disabled when created if needed
            this.updateButtonDisabled = !valid;
        }
    },

    afterRender: function() {
        var me = this,
            plugin = me.editingPlugin,
            grid = plugin.grid,
            view = grid.lockable ? grid.normalGrid.view : grid.view;

        me.callParent(arguments);

        // The scrollingViewEl is the TableView which scrolls
        me.scrollingView = view;
        me.scrollingViewEl = view.el;
        view.mon(me.scrollingViewEl, 'scroll', me.onViewScroll, me);

        // Prevent from bubbling click events to the grid view
        me.mon(me.el, {
            click: Ext.emptyFn,
            stopPropagation: true
        });

        // Ensure that the editor width always matches the total header width
        me.mon(grid, {
            resize: me.onGridResize,
            scope: me
        });

        me.el.swallowEvent([
            'keypress',
            'keydown'
        ]);

        me.fieldScroller = me.normalColumnContainer.layout.innerCt;
        me.fieldScroller.dom.style.overflow = 'hidden';
        me.fieldScroller.on({
            scroll: me.onFieldContainerScroll,
            scope: me
        });
        
        me.initKeyNav();

        me.mon(plugin.view, {
            beforerefresh: me.onBeforeViewRefresh,
            refresh: me.onViewRefresh,
            itemremove: me.onViewItemRemove,
            scope: me
        });

        // Prevent trying to reposition while we set everything up
        me.preventReposition = true;
        me.syncAllFieldWidths();
        delete me.preventReposition;    
    },
    
    initKeyNav: function() {
        var me = this,
            plugin = me.editingPlugin;
        
        me.keyNav = new Ext.util.KeyNav(me.el, {
            enter: plugin.onEnterKey,
            esc: plugin.onEscKey,
            scope: plugin
        });
    },

    onBeforeViewRefresh: function(view) {
        var me = this,
            viewDom = view.el.dom;

        if (me.el.dom.parentNode === viewDom) {
            viewDom.removeChild(me.el.dom);
        }
    },

    onViewRefresh: function(view) {
        var me = this,
            context = me.context,
            row;

        // Recover our row node after a view refresh
        if (context && (row = view.getNode(context.record, true))) {
            context.row = row;
            me.reposition();
            if (me.tooltip && me.tooltip.isVisible()) {
                me.tooltip.setTarget(context.row);
            }
        } else {
            me.editingPlugin.cancelEdit();
        }
    },

    onViewItemRemove: function(record, index) {
        var context = this.context;
        if (context && record === context.record) {
            // if the record being edited was removed, cancel editing
            this.editingPlugin.cancelEdit();
        }
    },

    onViewScroll: function() {
        var me = this,
            viewEl = me.editingPlugin.view.el,
            scrollingViewEl = me.scrollingViewEl,
            scrollTop  = scrollingViewEl.dom.scrollTop,
            scrollLeft = scrollingViewEl.getScrollLeft(),
            scrollLeftChanged = scrollLeft !== me.lastScrollLeft,
            scrollTopChanged = scrollTop !== me.lastScrollTop,
            row;

        me.lastScrollTop  = scrollTop;
        me.lastScrollLeft = scrollLeft;
        if (me.isVisible()) {
            row = Ext.getDom(me.context.row.id);

            // Only reposition if the row is in the DOM (buffered rendering may mean the context row is not there)
            if (row && viewEl.contains(row)) {
                if (scrollTopChanged) {

                    // The row element in the context may be stale due to buffered rendering removing out-of-view rows, then re-inserting newly rendered ones
                    me.context.row = row;
                    me.reposition(null, true);
                    if ((me.tooltip && me.tooltip.isVisible()) || me.hiddenTip) {
                        me.repositionTip();
                    }

                    me.syncEditorClip();
                }
            }
            // If row is NOT in the DOM, ensure the editor is out of sight
            else {
                me.setLocalY(-400);
            }
        }

        // Keep fields' left/right scroll position synced with view's left/right scroll
        if (me.rendered && scrollLeftChanged) {
            me.syncFieldsHorizontalScroll();
        }
    },

    // Synchronize the horizontal scroll position of the fields with the state of the grid view
    syncFieldsHorizontalScroll: function() {
        // Set overflow style here because it is an embedded element and the "style" Component config does not target it.
        this.fieldScroller.setScrollLeft(this.lastScrollLeft);
    },

    // Synchronize the horizontal scroll position of the grid view with the fields.
    onFieldContainerScroll: function() {
        this.scrollingViewEl.setScrollLeft(this.fieldScroller.getScrollLeft());
    },

    onColumnResize: function(column, width) {
        var me = this;

        if (me.rendered && !me.editingPlugin.reconfiguring) {
            // Need to ensure our lockable/normal horizontal scrollrange is set
            me.onGridResize();
            me.onViewScroll();
            if (!column.isGroupHeader) {
                me.syncFieldWidth(column);
                me.repositionIfVisible();
            }
        }
    },

    onColumnHide: function(column) {
        if (!this.editingPlugin.reconfiguring && !column.isGroupHeader) {
            column.getEditor().hide();
            this.repositionIfVisible();
        }
    },

    onColumnShow: function(column) {
        var me = this;

        if (me.rendered && !me.editingPlugin.reconfiguring && !column.isGroupHeader && column.getEditor) {
            column.getEditor().show();
            me.syncFieldWidth(column);
            if (!me.preventReposition) {
                this.repositionIfVisible();
            }
        }
    },

    onColumnMove: function(column, fromIdx, toIdx) {
        var me = this,
            i, incr = 1, len, field, fieldIdx,
            fieldContainer = column.isLocked() ? me.lockedColumnContainer : me.normalColumnContainer;

        // If moving a group, move each leaf header
        if (column.isGroupHeader) {
            Ext.suspendLayouts();
            column = column.getGridColumns();

            if (toIdx > fromIdx) {
                toIdx--;
                incr = 0;
            }

            this.addFieldsForColumn(column);
            for (i = 0, len = column.length; i < len; i++, fromIdx += incr, toIdx += incr) {
                field = column[i].getEditor();
                fieldIdx = fieldContainer.items.indexOf(field);

                // If the field is not in the container (moved from the main headerCt, INTO a group header)
                // then insert it into the correct place
                if (fieldIdx === -1) {
                    fieldContainer.insert(toIdx, field);
                }

                // If the field has not already been processed by an onColumnAdd (move from a group header INTO the main headerCt), then move it
                else if (fieldIdx != toIdx) {
                    fieldContainer.move(fromIdx, toIdx);
                }
            }
            Ext.resumeLayouts(true);
        } else {
            if (toIdx > fromIdx) {
                toIdx--;
            }
            this.addFieldsForColumn(column);
            field = column.getEditor();
            fieldIdx = fieldContainer.items.indexOf(field);
            if (fieldIdx === -1) {
                fieldContainer.insert(toIdx, field);
            }
            else if (fieldIdx != toIdx) {
                fieldContainer.move(fromIdx, toIdx);
            }
        }
    },

    onColumnAdd: function(column) {

        // If a column header added, process its leaves
        if (column.isGroupHeader) {
            column = column.getGridColumns();
        }
        //this.preventReposition = true;
        this.addFieldsForColumn(column);
        this.insertColumnEditor(column);
        this.preventReposition = false;
    },

    insertColumnEditor: function(column) {
        var me = this,
            plugin = me.editingPlugin,
            grid = plugin.grid,
            lockable = grid.lockable,
            fieldContainer,
            len, i;

        if (Ext.isArray(column)) {
            for (i = 0, len = column.length; i < len; i++) {
                me.insertColumnEditor(column[i]);
            }
            return;
        }

        if (!column.getEditor) {
            return;
        }

        if (column.isLocked()) {
            fieldContainer = me.lockedColumnContainer; 
            grid = grid.lockedGrid;
        } else {
            fieldContainer = me.normalColumnContainer;
            if (lockable) {
                grid = grid.normalGrid;
            }
        }

        // Insert the column's field into the editor panel.
        fieldContainer.insert(grid.getColumnManager().getHeaderIndex(column), column.getEditor());
    },

    onColumnRemove: function(ct, column) {
        column = column.isGroupHeader ? column.getGridColumns() : column;
        this.removeColumnEditor(column);
    },

    removeColumnEditor: function(column, destroy) {
        var me = this,
            field,
            len, i;

        if (Ext.isArray(column)) {
            for (i = 0, len = column.length; i < len; i++) {
                me.removeColumnEditor(column[i]);
            }
            return;
        }

        if (column.hasEditor() && column.field.isComponent) {
            field = column.getEditor();
            if (field) {

                // If the column is removed and destroyed (or we are being told to destroy), destroy the field.
                if (destroy || column.isDestroyed) {
                    field.destroy();
                }
                // Otherwise, just remove the field but preserve it for use when the column is reinserted.
                else if (field.ownerCt) {
                    field.ownerCt.remove(field, false);
                }
            }
        }
    },

    onColumnReplace: function(map, fieldId, column, oldColumn) {
        this.onColumnRemove(oldColumn.ownerCt, oldColumn);
    },

    getFloatingButtons: function() {
        var me = this,
            btns = me.floatingButtons;

        if (!btns) {
            me.floatingButtons = btns = new Ext.grid.RowEditorButtons({
                rowEditor: me
            });
        }
        return btns;
    },

    repositionIfVisible: function(c) {
        var me = this,
            view = me.view;

        // If we're showing ourselves, jump out
        // If the component we're showing doesn't contain the view
        if (c && (c == me || !c.el.isAncestor(view.el))) {
            return;
        }

        if (me.isVisible() && view.isVisible(true)) {
            me.reposition();
        }
    },

    getRefOwner: function() {
        return this.editingPlugin.grid;
    },

    // Lie to the CQ system about our nesting structure.
    // Pretend all the fields are always immediate children.
    // Include the two buttons.
    getRefItems: function(deep) {
        var me = this,
            result;

        if (me.lockable) {
            result = me.lockedColumnContainer.getRefItems(deep);
            result.push.apply(result, me.normalColumnContainer.getRefItems(deep));
        } else {
            result = me.callParent(arguments);
        }
        result.push.apply(result, me.getFloatingButtons().getRefItems(deep));
        return result;
    },

    reposition: function(animateConfig, fromScrollHandler) {
        var me = this,
            context = me.context,
            row = context && Ext.get(context.row),
            yOffset = 0,
            rowTop,
            localY,
            deltaY,
            afterPosition;

        // Position this editor if the context row is rendered (buffered rendering may mean that it's not in the DOM at all)
        if (row && Ext.isElement(row.dom)) {

            deltaY = me.syncButtonPosition(me.getScrollDelta());

            if (!me.editingPlugin.grid.rowLines) { 
                // When the grid does not have rowLines we add a bottom border to the previous
                // row when the row is focused, but subtract the border width from the 
                // top padding to keep the row from changing size.  This adjusts the top offset
                // of the cell edtor to account for the added border.
                yOffset = -parseInt(row.first().getStyle('border-bottom-width'), 10);
            }
            rowTop = me.calculateLocalRowTop(row);
            localY = me.calculateEditorTop(rowTop) + yOffset;

            // If not being called from scroll handler...
            // If the editor's top will end up above the fold
            // or the bottom will end up below the fold,
            // organize an afterPosition handler which will bring it into view and focus the correct input field
            if (!fromScrollHandler) {
                afterPosition = function() {
                    if (deltaY) {
                        me.scrollingViewEl.scrollBy(0, deltaY, true);
                    }
                    me.focusContextCell();
                }
            }

            me.syncEditorClip();

            // Get the y position of the row relative to its top-most static parent.
            // offsetTop will be relative to the table, and is incorrect
            // when mixed with certain grid features (e.g., grouping).
            if (animateConfig) {
                me.animate(Ext.applyIf({
                    to: {
                        top: localY
                    },
                    duration: animateConfig.duration || 125,
                    callback: afterPosition
                }, animateConfig));
            } else {
                me.setLocalY(localY);
                if (afterPosition) {
                    afterPosition();
                }
            }
        }
    },

    /**
     * @private
     * Returns the scroll delta required to scroll the context row into view in order to make
     * the whole of this editor visible.
     * @return {Number} the scroll delta. Zero if scrolling is not required.
     */
    getScrollDelta: function() {
        var me = this,
            scrollingViewDom = me.scrollingViewEl.dom,
            context = me.context,
            body = me.body,
            deltaY = 0;

        if (context) {
            deltaY = Ext.fly(context.row).getOffsetsTo(scrollingViewDom)[1] - body.getBorderPadding().beforeY;
            if (deltaY > 0) {
                deltaY = Math.max(deltaY + me.getHeight() + me.floatingButtons.getHeight() -
                    scrollingViewDom.clientHeight - body.getBorderWidth('b'), 0);
            }
        }
        return deltaY;
    },

    //
    // Calculates the top pixel position of the passed row within the view's scroll space.
    // So in a large, scrolled grid, this could be several thousand pixels.
    //
    calculateLocalRowTop: function(row) {
        var grid = this.editingPlugin.grid;
        return Ext.fly(row).getOffsetsTo(grid)[1] - grid.el.getBorderWidth('t') + this.lastScrollTop;
    },

    // Given the top pixel position of a row in the scroll space,
    // calculate the editor top position in the view's encapsulating element.
    // This will only ever be in the visible range of the view's element.
    calculateEditorTop: function(rowTop) {
        return rowTop - this.body.getBorderPadding().beforeY - this.lastScrollTop;
    },

    getClientWidth: function() {
        var me = this,
            grid = me.editingPlugin.grid,
            result;

        if (me.lockable) {
            result =
               grid.lockedGrid.getWidth() +
               grid.normalGrid.view.el.dom.clientWidth - 1;
        }
        else {
            result = grid.view.el.dom.clientWidth;
        }
        return result;
    },

    getEditor: function(fieldInfo) {
        var me = this;

        if (Ext.isNumber(fieldInfo)) {
            // In a locked grid, a RowEditor uses 2 inner containers, so need to use CQ to retrieve
            // configured editors which were stamped with the isEditorComponent property in Editing.createColumnField
            return me.query('[isEditorComponent]')[fieldInfo];
        } else if (fieldInfo.isHeader && !fieldInfo.isGroupHeader) {
            return fieldInfo.getEditor();
        }
    },    

    addFieldsForColumn: function(column, initial) {
        var me = this,
            i,
            length, field;

        if (Ext.isArray(column)) {
            for (i = 0, length = column.length; i < length; i++) {
                me.addFieldsForColumn(column[i], initial);
            }
            return;
        }

        if (column.getEditor) {

            // Get a default display field if necessary
            field = column.getEditor(null, me.getDefaultFieldCfg());
            if (column.align === 'right') {
                field.fieldStyle = 'text-align:right';
            }

            if (column.xtype === 'actioncolumn') {
                field.fieldCls += ' ' + Ext.baseCSSPrefix + 'form-action-col-field'
            }

            if (me.isVisible() && me.context) {
                if (field.is('displayfield')) {
                    me.renderColumnData(field, me.context.record, column);
                } else {
                    field.suspendEvents();
                    field.setValue(me.context.record.get(column.dataIndex));
                    field.resumeEvents();
                }
            }
            if (column.hidden) {
                me.onColumnHide(column);
            } else if (column.rendered && !initial) {
                // Setting after initial render
                me.onColumnShow(column);
            }
        }
    },
    
    getDefaultFieldCfg: function() {
        return {
            xtype: 'displayfield',
            // Override Field's implementation so that the default display fields will not return values. This is done because
            // the display field will pick up column renderers from the grid.
            getModelData: function() {
                return null;
            }
        };
    },

    loadRecord: function(record) {
        var me     = this,
            form   = me.getForm(),
            fields = form.getFields(),
            items  = fields.items,
            length = items.length,
            i, displayFields,
            isValid;

        // temporarily suspend events on form fields before loading record to prevent the fields' change events from firing
        for (i = 0; i < length; i++) {
            items[i].suspendEvents();
        }

        form.loadRecord(record);

        for (i = 0; i < length; i++) {
            items[i].resumeEvents();
        }

        isValid = form.isValid();
        if (me.errorSummary) {
            if (isValid) {
                me.hideToolTip();
            } else {
                me.showToolTip();
            }
        }
        me.updateButton(isValid);

        // render display fields so they honor the column renderer/template
        displayFields = me.query('>displayfield');
        length = displayFields.length;

        for (i = 0; i < length; i++) {
            me.renderColumnData(displayFields[i], record);
        }
    },

    renderColumnData: function(field, record, activeColumn) {
        var me = this,
            grid = me.editingPlugin.grid,
            headerCt = grid.headerCt,
            view = me.scrollingView,
            store = view.dataSource,
            column = activeColumn || field.column,
            value = record.get(column.dataIndex),
            renderer = column.editRenderer || column.renderer,
            metaData,
            rowIdx,
            colIdx;

        // honor our column's renderer (TemplateHeader sets renderer for us!)
        if (renderer) {
            metaData = { tdCls: '', style: '' };
            rowIdx = store.indexOf(record);
            colIdx = headerCt.getHeaderIndex(column);

            value = renderer.call(
                column.scope || headerCt.ownerCt,
                value,
                metaData,
                record,
                rowIdx,
                colIdx,
                store,
                view
            );
        }

        field.setRawValue(value);
        field.resetOriginalValue();
    },

    beforeEdit: function() {
        var me = this,
            scrollDelta;

        if (me.isVisible() && me.errorSummary && !me.autoCancel && me.isDirty()) {

            // Scroll the visible RowEditor that is in error state back into view
            scrollDelta = me.getScrollDelta();
            if (scrollDelta) {
                me.scrollingViewEl.scrollBy(0, scrollDelta, true)
            }
            me.showToolTip();
            return false;
        }
    },

    /**
     * Start editing the specified grid at the specified position.
     * @param {Ext.data.Model} record The Store data record which backs the row to be edited.
     * @param {Ext.data.Model} columnHeader The Column object defining the column to be focused
     */
    startEdit: function(record, columnHeader) {
        var me = this,
            editingPlugin = me.editingPlugin,
            grid = editingPlugin.grid,
            context = me.context = editingPlugin.context;

        if (!me.rendered) {
            me.width = me.getClientWidth();
            me.render(grid.el, grid.el.dom.firstChild);
            me.getFloatingButtons().render(me.el);
            // On first show we need to ensure that we have the scroll positions cached
            me.onViewScroll();
        } else {
            me.syncFieldsHorizontalScroll();
        }
        
        // Select the record before showing the editor, since
        // selecting will steal focus
        context.grid.getSelectionModel().select(record);

        if (me.isVisible()) {
            me.reposition(true);
        } else {
            me.show();
        }

        // Make sure the container el is correctly sized.
        me.onGridResize();

        // Reload the record data
        me.loadRecord(record);
    },

    // determines the amount by which the row editor will overflow, and flips the buttons
    // to the top of the editor if the required scroll amount is greater than the available
    // scroll space. Returns the scrollDelta required to scroll the editor into view after
    // adjusting the button position.
    syncButtonPosition: function(scrollDelta) {
        var me = this,
            floatingButtons = me.getFloatingButtons(),
            scrollingViewElDom = me.scrollingViewEl.dom,
            overflow = this.getScrollDelta() - (scrollingViewElDom.scrollHeight -
                scrollingViewElDom.scrollTop - scrollingViewElDom.clientHeight);

        if (overflow > 0) {
            if (!me._buttonsOnTop) {
                floatingButtons.setButtonPosition('top');
                me._buttonsOnTop = true;
            }
            scrollDelta = 0;
        } else if (me._buttonsOnTop !== false) {
            floatingButtons.setButtonPosition('bottom');
            me._buttonsOnTop = false;
        }

        return scrollDelta;
    },

    // since the editor is rendered to the grid el, it must be clipped when scrolled
    // outside of the grid view area so that it does not overlap the scrollbar or docked items
    syncEditorClip: function() {
        var me = this,
            overflow = me.getScrollDelta(),
            btnHeight;

        if (overflow) {
            // The editor is overflowing outside of the view area, either above or below
            me.isOverflowing = true;
            btnHeight = me.floatingButtons.getHeight();

            if (overflow > 0) {
                // editor is overflowing the bottom of the view
                me.clipBottom(Math.max(me.getHeight() - overflow + btnHeight, -btnHeight));
            } else if (overflow < 0) {
                // editor is overflowing the top of the view
                overflow = Math.abs(overflow);
                me.clipTop(Math.max(overflow, 0));
            }
        } else if (me.isOverflowing) {
            me.clearClip();
            me.isOverflowing = false;
        }
    },

    // Focus the cell on start edit based upon the current context
    focusContextCell: function() {
        var column = this.context.column,
            field;
        
        if (!column.isDestroyed) {   
            field = this.getEditor(column);
            if (field && field.focus) {
                field.focus();
            }
        }
    },

    cancelEdit: function() {
        var me     = this,
            form   = me.getForm(),
            fields = form.getFields(),
            items  = fields.items,
            length = items.length,
            i;

        me.hide();
        form.clearInvalid();

        // temporarily suspend events on form fields before reseting the form to prevent the fields' change events from firing
        for (i = 0; i < length; i++) {
            items[i].suspendEvents();
        }

        form.reset();

        for (i = 0; i < length; i++) {
            items[i].resumeEvents();
        }
    },

    completeEdit: function() {
        var me = this,
            form = me.getForm();

        if (!form.isValid()) {
            return false;
        }

        form.updateRecord(me.context.record);
        me.hide();
        return true;
    },

    onShow: function() {
        var me = this;
        
        me.callParent(arguments);
        if (me.needsSyncFieldWidths) {
            me.suspendLayouts();
            me.syncAllFieldWidths();
            me.resumeLayouts(true);
        }
        delete me.needsSyncFieldWidths;

        me.reposition();
    },

    onHide: function() {
        var me = this;

        me.callParent(arguments);
        if (me.tooltip) {
            me.hideToolTip();
        }
        if (me.context) {
            me.context.view.focusRow(me.context.record);
            me.context = null;
        }
    },

    isDirty: function() {
        var me = this,
            form = me.getForm();
        return form.isDirty();
    },

    getToolTip: function() {
        return this.tooltip || (this.tooltip = new Ext.tip.ToolTip({
            cls: Ext.baseCSSPrefix + 'grid-row-editor-errors',
            title: this.errorsText,
            autoHide: false,
            closable: true,
            closeAction: 'disable',
            anchor: 'left',
            anchorToTarget: false
        }));
    },

    hideToolTip: function() {
        var me = this,
            tip = me.getToolTip();
        if (tip.rendered) {
            tip.disable();
        }
        me.hiddenTip = false;
    },

    showToolTip: function() {
        var me = this,
            tip = me.getToolTip();

        tip.showAt([0, 0]);
        tip.update(me.getErrors());
        me.repositionTip();
        tip.enable();
    },

    repositionTip: function() {
        var me = this,
            tip = me.getToolTip(),
            context = me.context,
            row = Ext.get(context.row),
            viewEl = me.scrollingViewEl,
            viewHeight = viewEl.dom.clientHeight,
            viewTop = me.lastScrollTop,
            viewBottom = viewTop + viewHeight,
            rowHeight = row.getHeight(),
            rowTop = row.getOffsetsTo(me.context.view.body)[1],
            rowBottom = rowTop + rowHeight;

        if (rowBottom > viewTop && rowTop < viewBottom) {
            tip.showAt(tip.getAlignToXY(viewEl, 'tl-tr', [15, row.getOffsetsTo(viewEl)[1]]));
            me.hiddenTip = false;
        } else {
            tip.hide();
            me.hiddenTip = true;
        }
    },

    getErrors: function() {
        var me        = this,
            errors    = [],
            fields    = me.query('>[isFormField]'),
            length    = fields.length,
            i;

        for (i = 0; i < length; i++) {
            errors = errors.concat(
                Ext.Array.map(fields[i].getErrors(), me.createErrorListItem)
            );
        }

        // Only complain about unsaved changes if all the fields are valid
        if (!errors.length && !me.autoCancel && me.isDirty()) {
            errors[0] = me.createErrorListItem(me.dirtyText);
        }

        return '<ul class="' + Ext.plainListCls + '">' + errors.join('') + '</ul>';
    },

    createErrorListItem: function(e) {
        return '<li class="' + this.errorCls + '">' + e + '</li>';
    },

    beforeDestroy: function(){
        Ext.destroy(this.floatingButtons, this.tooltip);
        this.callParent();    
    },

    clipBottom: function(value) {
        this.el.setStyle('clip', 'rect(-1000px auto ' + value + 'px auto)');
    },

    clipTop: function(value) {
        this.el.setStyle('clip', 'rect(' + value + 'px auto 1000px auto)');
    },

    clearClip: function(el) {
        this.el.setStyle(
            'clip',
            Ext.isIE8m || Ext.isIEQuirks ? 'rect(-1000px auto 1000px auto)' : 'auto'
        );
    }
});


/**
 * @private
 */
Ext.define('Ext.view.DropZone', {
    extend:  Ext.dd.DropZone ,

    indicatorCls: Ext.baseCSSPrefix + 'grid-drop-indicator',
    indicatorHtml: [
        '<div class="', Ext.baseCSSPrefix, 'grid-drop-indicator-left" role="presentation"></div>',
        '<div class="' + Ext.baseCSSPrefix + 'grid-drop-indicator-right" role="presentation"></div>'
    ].join(''),

    constructor: function(config) {
        var me = this;
        Ext.apply(me, config);

        // Create a ddGroup unless one has been configured.
        // User configuration of ddGroups allows users to specify which
        // DD instances can interact with each other. Using one
        // based on the id of the View would isolate it and mean it can only
        // interact with a DragZone on the same View also using a generated ID.
        if (!me.ddGroup) {
            me.ddGroup = 'view-dd-zone-' + me.view.id;
        }

        // The DropZone's encapsulating element is the View's main element. It must be this because drop gestures
        // may require scrolling on hover near a scrolling boundary. In Ext 4.x two DD instances may not use the
        // same element, so a DragZone on this same View must use the View's parent element as its element.
        me.callParent([me.view.el]);
    },

//  Fire an event through the client DataView. Lock this DropZone during the event processing so that
//  its data does not become corrupted by processing mouse events.
    fireViewEvent: function() {
        var me = this,
            result;

        me.lock();
        result = me.view.fireEvent.apply(me.view, arguments);
        me.unlock();
        return result;
    },

    getTargetFromEvent : function(e) {
        var node = e.getTarget(this.view.getItemSelector()),
            mouseY, nodeList, testNode, i, len, box;

//      Not over a row node: The content may be narrower than the View's encapsulating element, so return the closest.
//      If we fall through because the mouse is below the nodes (or there are no nodes), we'll get an onContainerOver call.
        if (!node) {
            mouseY = e.getPageY();
            for (i = 0, nodeList = this.view.getNodes(), len = nodeList.length; i < len; i++) {
                testNode = nodeList[i];
                box = Ext.fly(testNode).getBox();
                if (mouseY <= box.bottom) {
                    return testNode;
                }
            }
        }
        return node;
    },

    getIndicator: function() {
        var me = this;

        if (!me.indicator) {
            me.indicator = new Ext.Component({
                ariaRole: 'presentation',
                html: me.indicatorHtml,
                cls: me.indicatorCls,
                ownerCt: me.view,
                floating: true,
                shadow: false
            });
        }
        return me.indicator;
    },

    getPosition: function(e, node) {
        var y      = e.getXY()[1],
            region = Ext.fly(node).getRegion(),
            pos;

        if ((region.bottom - y) >= (region.bottom - region.top) / 2) {
            pos = "before";
        } else {
            pos = "after";
        }
        return pos;
    },

    /**
     * @private Determines whether the record at the specified offset from the passed record
     * is in the drag payload.
     * @param records
     * @param record
     * @param offset
     * @returns {Boolean} True if the targeted record is in the drag payload
     */
    containsRecordAtOffset: function(records, record, offset) {
        if (!record) {
            return false;
        }
        var view = this.view,
            recordIndex = view.indexOf(record),
            nodeBefore = view.getNode(recordIndex + offset, true),
            recordBefore = nodeBefore ? view.getRecord(nodeBefore) : null;

        return recordBefore && Ext.Array.contains(records, recordBefore);
    },

    positionIndicator: function(node, data, e) {
        var me = this,
            view = me.view,
            pos = me.getPosition(e, node),
            overRecord = view.getRecord(node),
            draggingRecords = data.records,
            indicatorY;

        if (!Ext.Array.contains(draggingRecords, overRecord) && (
            pos == 'before' && !me.containsRecordAtOffset(draggingRecords, overRecord, -1) ||
            pos == 'after' && !me.containsRecordAtOffset(draggingRecords, overRecord, 1)
        )) {
            me.valid = true;

            if (me.overRecord != overRecord || me.currentPosition != pos) {

                indicatorY = Ext.fly(node).getY() - view.el.getY() - 1;
                if (pos == 'after') {
                    indicatorY += Ext.fly(node).getHeight();
                }
                me.getIndicator().setWidth(Ext.fly(view.el).getWidth()).showAt(0, indicatorY);

                // Cache the overRecord and the 'before' or 'after' indicator.
                me.overRecord = overRecord;
                me.currentPosition = pos;
            }
        } else {
            me.invalidateDrop();
        }
    },

    invalidateDrop: function() {
        if (this.valid) {
            this.valid = false;
            this.getIndicator().hide();
        }
    },

    // The mouse is over a View node
    onNodeOver: function(node, dragZone, e, data) {
        var me = this;

        if (!Ext.Array.contains(data.records, me.view.getRecord(node))) {
            me.positionIndicator(node, data, e);
        }
        return me.valid ? me.dropAllowed : me.dropNotAllowed;
    },

    // Moved out of the DropZone without dropping.
    // Remove drop position indicator
    notifyOut: function(node, dragZone, e, data) {
        var me = this;

        me.callParent(arguments);
        me.overRecord = me.currentPosition = null
        me.valid = false;
        if (me.indicator) {
            me.indicator.hide();
        }
    },

    // The mouse is past the end of all nodes (or there are no nodes)
    onContainerOver : function(dd, e, data) {
        var me = this,
            view = me.view,
            count = view.dataSource.getCount();

        // There are records, so position after the last one
        if (count) {
            me.positionIndicator(view.all.last(), data, e);
        }

        // No records, position the indicator at the top
        else {
            me.overRecord = me.currentPosition = null;
            me.getIndicator().setWidth(Ext.fly(view.el).getWidth()).showAt(0, 0);
            me.valid = true;
        }
        return me.dropAllowed;
    },

    onContainerDrop : function(dd, e, data) {
        return this.onNodeDrop(dd, null, e, data);
    },

    onNodeDrop: function(targetNode, dragZone, e, data) {
        var me = this,
            dropHandled = false,
 
            // Create a closure to perform the operation which the event handler may use.
            // Users may now set the wait parameter in the beforedrop handler, and perform any kind
            // of asynchronous processing such as an Ext.Msg.confirm, or an Ajax request,
            // and complete the drop gesture at some point in the future by calling either the
            // processDrop or cancelDrop methods.
            dropHandlers = {
                wait: false,
                processDrop: function () {
                    me.invalidateDrop();
                    me.handleNodeDrop(data, me.overRecord, me.currentPosition);
                    dropHandled = true;
                    me.fireViewEvent('drop', targetNode, data, me.overRecord, me.currentPosition);
                },
 
                cancelDrop: function() {
                    me.invalidateDrop();
                    dropHandled = true;
                }
            },
            performOperation = false;
 
        if (me.valid) {
            performOperation = me.fireViewEvent('beforedrop', targetNode, data, me.overRecord, me.currentPosition, dropHandlers);
            if (dropHandlers.wait) {
                return;
            }
 
            if (performOperation !== false) {
                // If either of the drop handlers were called in the event handler, do not do it again.
                if (!dropHandled) {
                    dropHandlers.processDrop();
                }
            }
        }
        return performOperation;
    },
    
    destroy: function(){
        Ext.destroy(this.indicator);
        delete this.indicator;
        this.callParent();
    }
});

/**
 * @private
 */
Ext.define('Ext.grid.ViewDropZone', {
    extend:  Ext.view.DropZone ,

    indicatorHtml: '<div class="' + Ext.baseCSSPrefix + 'grid-drop-indicator-left" role="presentation"></div><div class="' + Ext.baseCSSPrefix + 'grid-drop-indicator-right" role="presentation"></div>',
    indicatorCls: Ext.baseCSSPrefix + 'grid-drop-indicator',

    handleNodeDrop : function(data, record, position) {
        var view = this.view,
            store = view.getStore(),
            index, records, i, len;

        // If the copy flag is set, create a copy of the models
        if (data.copy) {
            records = data.records;
            data.records = [];
            for (i = 0, len = records.length; i < len; i++) {
                data.records.push(records[i].copy());
            }
        } else {
            /*
             * Remove from the source store. We do this regardless of whether the store
             * is the same bacsue the store currently doesn't handle moving records
             * within the store. In the future it should be possible to do this.
             * Here was pass the isMove parameter if we're moving to the same view.
             */
            data.view.store.remove(data.records, data.view === view);
        }

        if (record && position) {
            index = store.indexOf(record);

            // 'after', or undefined (meaning a drop at index -1 on an empty View)...
            if (position !== 'before') {
                index++;
            }
            store.insert(index, data.records);
        }
        // No position specified - append.
        else {
            store.add(data.records);
        }

        view.getSelectionModel().select(data.records);
    }
});

/**
 * Plugin to add header resizing functionality to a HeaderContainer.
 * Always resizing header to the left of the splitter you are resizing.
 */
Ext.define('Ext.grid.plugin.HeaderResizer', {
    extend:  Ext.AbstractPlugin ,
                                                        
    alias: 'plugin.gridheaderresizer',

    disabled: false,

    config: {
        /**
         * @cfg {Boolean} dynamic
         * True to resize on the fly rather than using a proxy marker.
         * @accessor
         */
        dynamic: false
    },

    colHeaderCls: Ext.baseCSSPrefix + 'column-header',

    minColWidth: 40,
    maxColWidth: 1000,
    wResizeCursor: 'col-resize',
    eResizeCursor: 'col-resize',
    // not using w and e resize bc we are only ever resizing one
    // column
    //wResizeCursor: Ext.isWebKit ? 'w-resize' : 'col-resize',
    //eResizeCursor: Ext.isWebKit ? 'e-resize' : 'col-resize',

    init: function(headerCt) {
        this.headerCt = headerCt;
        headerCt.on('render', this.afterHeaderRender, this, {single: true});
    },

    /**
     * @private
     * AbstractComponent calls destroy on all its plugins at destroy time.
     */
    destroy: function() {
        var tracker = this.tracker;
        if (tracker) {
            delete tracker.onBeforeStart;
            delete tracker.onStart;
            delete tracker.onDrag;
            delete tracker.onEnd;
            tracker.destroy();
            this.tracker = null;
        }
    },

    afterHeaderRender: function() {
        var headerCt = this.headerCt,
            el = headerCt.el;

        headerCt.mon(el, 'mousemove', this.onHeaderCtMouseMove, this);

        this.tracker = new Ext.dd.DragTracker({
            disabled: this.disabled,
            onBeforeStart: Ext.Function.bind(this.onBeforeStart, this),
            onStart: Ext.Function.bind(this.onStart, this),
            onDrag: Ext.Function.bind(this.onDrag, this),
            onEnd: Ext.Function.bind(this.onEnd, this),
            tolerance: 3,
            autoStart: 300,
            el: el
        });
    },

    // As we mouse over individual headers, change the cursor to indicate
    // that resizing is available, and cache the resize target header for use
    // if/when they mousedown.
    onHeaderCtMouseMove: function(e, t) {
        var me = this,
            prevSiblings,
            headerEl, overHeader, resizeHeader, resizeHeaderOwnerGrid, ownerGrid;

        if (me.headerCt.dragging) {
            if (me.activeHd) {
                me.activeHd.el.dom.style.cursor = '';
                delete me.activeHd;
            }
        } else {
            headerEl = e.getTarget('.' + me.colHeaderCls, 3, true);

            if (headerEl){
                overHeader = Ext.getCmp(headerEl.id);

                // On left edge, go back to the previous non-hidden header.
                if (overHeader.isOnLeftEdge(e)) {
                    resizeHeader = overHeader.previousNode('gridcolumn:not([hidden]):not([isGroupHeader])')
                    // There may not *be* a previous non-hidden header.
                    if (resizeHeader) {

                        ownerGrid = me.headerCt.up('tablepanel');
                        resizeHeaderOwnerGrid = resizeHeader.up('tablepanel');

                        // Need to check that previousNode didn't go outside the current grid/tree
                        // But in the case of a Grid which contains a locked and normal grid, allow previousNode to jump
                        // from the first column of the normalGrid to the last column of the lockedGrid
                        if (!((resizeHeaderOwnerGrid === ownerGrid) || ((ownerGrid.ownerCt.isXType('tablepanel')) && ownerGrid.ownerCt.view.lockedGrid === resizeHeaderOwnerGrid))) {
                            resizeHeader = null;
                        }
                    }
                }
                // Else, if on the right edge, we're resizing the column we are over
                else if (overHeader.isOnRightEdge(e)) {
                    resizeHeader = overHeader;
                }
                // Between the edges: we are not resizing
                else {
                    resizeHeader = null;
                }

                // We *are* resizing
                if (resizeHeader) {
                    // If we're attempting to resize a group header, that cannot be resized,
                    // so find its last visible leaf header; Group headers are sized
                    // by the size of their child headers.
                    if (resizeHeader.isGroupHeader) {
                        prevSiblings = resizeHeader.getGridColumns();
                        resizeHeader = prevSiblings[prevSiblings.length - 1];
                    }

                    // Check if the header is resizable. Continue checking the old "fixed" property, bug also
                    // check whether the resizable property is set to false.
                    if (resizeHeader && !(resizeHeader.fixed || (resizeHeader.resizable === false) || me.disabled)) {
                        me.activeHd = resizeHeader;
                        overHeader.el.dom.style.cursor = me.eResizeCursor;
                        if (overHeader.triggerEl) {
                            overHeader.triggerEl.dom.style.cursor = me.eResizeCursor;
                        }
                    }
                // reset
                } else {
                    overHeader.el.dom.style.cursor = '';
                    if (overHeader.triggerEl) {
                        overHeader.triggerEl.dom.style.cursor = '';
                    }
                    me.activeHd = null;
                }
            }
        }
    },

    // only start when there is an activeHd
    onBeforeStart : function(e) {
        // cache the activeHd because it will be cleared.
        this.dragHd = this.activeHd;

        if (!!this.dragHd && !this.headerCt.dragging) {
            this.tracker.constrainTo = this.getConstrainRegion();
            return true;
        } else {
            this.headerCt.dragging = false;
            return false;
        }
    },

    // get the region to constrain to, takes into account max and min col widths
    getConstrainRegion: function() {
        var me       = this,
            dragHdEl = me.dragHd.el,
            rightAdjust = 0,
            nextHd,
            lockedGrid;

        // If forceFit, then right constraint is based upon not being able to force the next header
        // beyond the minColWidth. If there is no next header, then the header may not be expanded.
        if (me.headerCt.forceFit) {
            nextHd = me.dragHd.nextNode('gridcolumn:not([hidden]):not([isGroupHeader])');
            if (nextHd) {
                if (!me.headerInSameGrid(nextHd)) {
                    nextHd = null;
                }
                rightAdjust = nextHd.getWidth() - me.minColWidth;
            }
        }

        // If resize header is in a locked grid, the maxWidth has to be 30px within the available locking grid's width
        else if ((lockedGrid = me.dragHd.up('tablepanel')).isLocked) {
            rightAdjust = me.dragHd.up('[scrollerOwner]').getWidth() - lockedGrid.getWidth() - 30;
        }

        // Else ue our default max width
        else {
            rightAdjust = me.maxColWidth - dragHdEl.getWidth();
        }

        return me.adjustConstrainRegion(
            dragHdEl.getRegion(),
            0,
            rightAdjust,
            0,
            me.minColWidth
        );
    },

    // initialize the left and right hand side markers around
    // the header that we are resizing
    onStart: function(e){
        var me       = this,
            dragHd   = me.dragHd,
            width    = dragHd.el.getWidth(),
            headerCt = dragHd.getOwnerHeaderCt(),
            x, y, gridSection, markerOwner, lhsMarker, rhsMarker, markerHeight;

        me.headerCt.dragging = true;
        me.origWidth = width;

        // setup marker proxies
        if (!me.dynamic) {
            gridSection  = markerOwner = headerCt.up('tablepanel');
            if (gridSection.ownerLockable) {
                markerOwner = gridSection.ownerLockable;
            }
            x            = me.getLeftMarkerX(markerOwner);
            lhsMarker    = markerOwner.getLhsMarker();
            rhsMarker    = markerOwner.getRhsMarker();
            markerHeight = gridSection.body.getHeight() + headerCt.getHeight();
            y            = headerCt.getOffsetsTo(markerOwner)[1];

            lhsMarker.setLocalY(y);
            rhsMarker.setLocalY(y);
            lhsMarker.setHeight(markerHeight);
            rhsMarker.setHeight(markerHeight);
            me.setMarkerX(lhsMarker, x);
            me.setMarkerX(rhsMarker, x + width);
        }
    },

    // synchronize the rhsMarker with the mouse movement
    onDrag: function(e){
        var me = this,
            markerOwner;
            
        if (me.dynamic) {
            me.doResize();
        } else {
            markerOwner = this.headerCt.up('tablepanel');
            if (markerOwner.ownerLockable) {
                markerOwner = markerOwner.ownerLockable;
            }
            this.setMarkerX(this.getMovingMarker(markerOwner), this.calculateDragX(markerOwner));
        }
    },
    
    getMovingMarker: function(markerOwner){
        return markerOwner.getRhsMarker();
    },

    onEnd: function(e) {
        this.headerCt.dragging = false;
        if (this.dragHd) {
            if (!this.dynamic) {
                var markerOwner = this.headerCt.up('tablepanel');

                // hide markers
                if (markerOwner.ownerLockable) {
                    markerOwner = markerOwner.ownerLockable;
                }
                this.setMarkerX(markerOwner.getLhsMarker(), -9999);
                this.setMarkerX(markerOwner.getRhsMarker(), -9999);
            }
            this.doResize();
        }
    },

    doResize: function() {
        var me = this,
            dragHd = me.dragHd,
            nextHd,
            offset;
            
        if (dragHd) {
            offset = me.tracker.getOffset('point');

            // resize the dragHd
            if (dragHd.flex) {
                delete dragHd.flex;
            }

            Ext.suspendLayouts();

            // Set the new column width.
            me.adjustColumnWidth(offset[0]);
 
            // In the case of forceFit, change the following Header width.
            // Constraining so that neither neighbour can be sized to below minWidth is handled in getConstrainRegion
            if (me.headerCt.forceFit) {
                nextHd = dragHd.nextNode('gridcolumn:not([hidden]):not([isGroupHeader])');
                if (nextHd && !me.headerInSameGrid(nextHd)) {
                    nextHd = null;
                }
                if (nextHd) {
                    delete nextHd.flex;
                    nextHd.setWidth(nextHd.getWidth() - offset[0]);
                }
            }

            // Apply the two width changes by laying out the owning HeaderContainer
            Ext.resumeLayouts(true);
        }
    },
    
    // nextNode can traverse out of this grid, possibly to others on the page, so limit it here
    headerInSameGrid: function(header) {
        var grid = this.dragHd.up('tablepanel');
        
        return !!header.up(grid);
    },

    disable: function() {
        this.disabled = true;
        if (this.tracker) {
            this.tracker.disable();
        }
    },

    enable: function() {
        this.disabled = false;
        if (this.tracker) {
            this.tracker.enable();
        }
    },

    calculateDragX: function(markerOwner) {
        return this.tracker.getXY('point')[0] - markerOwner.getX() - markerOwner.el.getBorderWidth('l');
    },

    getLeftMarkerX: function(markerOwner) {
        return this.dragHd.getX() - markerOwner.getX() - markerOwner.el.getBorderWidth('l') - 1;
    },

    setMarkerX: function(marker, x) {
        marker.setLocalX(x);
    },

    adjustConstrainRegion: function(region, t, r, b, l) {
        return region.adjust(t, r, b, l);
    },

    adjustColumnWidth: function(offsetX) {
        this.dragHd.setWidth(this.origWidth + offsetX);
    }
});

/**
 * @private
 */
Ext.define('Ext.grid.header.DragZone', {
    extend:  Ext.dd.DragZone ,
    colHeaderSelector: '.' + Ext.baseCSSPrefix + 'column-header',
    colInnerSelector: '.' + Ext.baseCSSPrefix + 'column-header-inner',
    maxProxyWidth: 120,

    constructor: function(headerCt) {
        var me = this;
        
        me.headerCt = headerCt;
        me.ddGroup =  me.getDDGroup();
        me.autoGroup = true;
        me.callParent([headerCt.el]);
        me.proxy.el.addCls(Ext.baseCSSPrefix + 'grid-col-dd');
    },
    
    getDDGroup: function() {
        return 'header-dd-zone-' + this.headerCt.up('[scrollerOwner]').id;
    },

    getDragData: function(e) {
        if (e.getTarget(this.colInnerSelector)) {
            var header = e.getTarget(this.colHeaderSelector),
                headerCmp,
                ddel;

            if (header) {
                headerCmp = Ext.getCmp(header.id);
                if (!this.headerCt.dragging && headerCmp.draggable && !(headerCmp.isOnLeftEdge(e) || headerCmp.isOnRightEdge(e))) {
                    ddel = document.createElement('div');
                    ddel.role = 'presentation';
                    ddel.innerHTML = Ext.getCmp(header.id).text;
                    return {
                        ddel: ddel,
                        header: headerCmp
                    };
                }
            }
        }
        return false;
    },

    onBeforeDrag: function() {
        return !(this.headerCt.dragging || this.disabled);
    },

    onInitDrag: function() {
        this.headerCt.dragging = true;
        this.callParent(arguments);
    },

    onDragDrop: function() {
        this.headerCt.dragging = false;
        this.callParent(arguments);
    },

    afterRepair: function() {
        this.callParent();
        this.headerCt.dragging = false;
    },

    getRepairXY: function() {
        return this.dragData.header.el.getXY();
    },
    
    disable: function() {
        this.disabled = true;
    },
    
    enable: function() {
        this.disabled = false;
    }
});

/**
 * @private
 */
Ext.define('Ext.grid.header.DropZone', {
    extend:  Ext.dd.DropZone ,
    colHeaderCls: Ext.baseCSSPrefix + 'column-header',
    proxyOffsets: [-4, -9],

    constructor: function(headerCt) {
        var me = this;
        
        me.headerCt = headerCt;
        me.ddGroup = me.getDDGroup();
        me.autoGroup = true;
        me.callParent([headerCt.el]);
    },

    getDDGroup: function() {
        return 'header-dd-zone-' + this.headerCt.up('[scrollerOwner]').id;
    },

    getTargetFromEvent : function(e){
        return e.getTarget('.' + this.colHeaderCls);
    },

    getTopIndicator: function() {
        if (!this.topIndicator) {
            this.self.prototype.topIndicator = Ext.DomHelper.append(Ext.getBody(), {
                role: 'presentation',
                cls: "col-move-top",
                html: "&#160;"
            }, true);
            this.self.prototype.indicatorXOffset = Math.floor((this.topIndicator.dom.offsetWidth + 1) / 2);
        }
        return this.topIndicator;
    },

    getBottomIndicator: function() {
        if (!this.bottomIndicator) {
            this.self.prototype.bottomIndicator = Ext.DomHelper.append(Ext.getBody(), {
                role: 'presentation',
                cls: "col-move-bottom",
                html: "&#160;"
            }, true);
        }
        return this.bottomIndicator;
    },

    getLocation: function(e, t) {
        var x      = e.getXY()[0],
            region = Ext.fly(t).getRegion(),
            pos;

        if ((region.right - x) <= (region.right - region.left) / 2) {
            pos = "after";
        } else {
            pos = "before";
        }
        return {
            pos: pos,
            header: Ext.getCmp(t.id),
            node: t
        };
    },

    positionIndicator: function(data, node, e){
        var me = this,
            dragHeader   = data.header,
            dropLocation = me.getLocation(e, node),
            targetHeader = dropLocation.header,
            pos          = dropLocation.pos,
            nextHd,
            prevHd,
            topIndicator, bottomIndicator, topAnchor, bottomAnchor,
            topXY, bottomXY, headerCtEl, minX, maxX,
            allDropZones, ln, i, dropZone;

        // Avoid expensive CQ lookups and DOM calculations if dropPosition has not changed
        if (targetHeader === me.lastTargetHeader && pos === me.lastDropPos) {
            return;
        }
        nextHd       = dragHeader.nextSibling('gridcolumn:not([hidden])');
        prevHd       = dragHeader.previousSibling('gridcolumn:not([hidden])');
        me.lastTargetHeader = targetHeader;
        me.lastDropPos = pos;

        // Cannot drag to before non-draggable start column
        if (!targetHeader.draggable && pos === 'before' && targetHeader.getIndex() === 0) {
            return false;
        }

        data.dropLocation = dropLocation;

        if ((dragHeader !== targetHeader) &&
            ((pos === "before" && nextHd !== targetHeader) ||
            (pos === "after" && prevHd !== targetHeader)) &&
            !targetHeader.isDescendantOf(dragHeader)) {

            // As we move in between different DropZones that are in the same
            // group (such as the case when in a locked grid), invalidateDrop
            // on the other dropZones.
            allDropZones = Ext.dd.DragDropManager.getRelated(me);
            ln = allDropZones.length;
            i  = 0;

            for (; i < ln; i++) {
                dropZone = allDropZones[i];
                if (dropZone !== me && dropZone.invalidateDrop) {
                    dropZone.invalidateDrop();
                }
            }

            me.valid = true;
            topIndicator = me.getTopIndicator();
            bottomIndicator = me.getBottomIndicator();
            if (pos === 'before') {
                topAnchor = 'bc-tl';
                bottomAnchor = 'tc-bl';
            } else {
                topAnchor = 'bc-tr';
                bottomAnchor = 'tc-br';
            }
            
            // Calculate arrow positions. Offset them to align exactly with column border line
            topXY = topIndicator.getAlignToXY(targetHeader.el, topAnchor);
            bottomXY = bottomIndicator.getAlignToXY(targetHeader.el, bottomAnchor);

            // constrain the indicators to the viewable section
            headerCtEl = me.headerCt.el;
            minX = headerCtEl.getX() - me.indicatorXOffset;
            maxX = headerCtEl.getX() + headerCtEl.getWidth();

            topXY[0] = Ext.Number.constrain(topXY[0], minX, maxX);
            bottomXY[0] = Ext.Number.constrain(bottomXY[0], minX, maxX);

            // position and show indicators
            topIndicator.setXY(topXY);
            bottomIndicator.setXY(bottomXY);
            topIndicator.show();
            bottomIndicator.show();

        // invalidate drop operation and hide indicators
        } else {
            me.invalidateDrop();
        }
    },

    invalidateDrop: function() {
        this.valid = false;
        this.hideIndicators();
    },

    onNodeOver: function(node, dragZone, e, data) {
        var me = this,
            from = data.header,
            doPosition,
            to,
            fromPanel,
            toPanel;

        if (data.header.el.dom === node) {
            doPosition = false;
        } else {
            data.isLock = data.isUnlock = false;
            to = me.getLocation(e, node).header;
            
            // Dragging within the same container - always valid
            doPosition = (from.ownerCt === to.ownerCt);

            // If from different containers, and they are not sealed, then continue checking
            if (!doPosition && (!from.ownerCt.sealed && !to.ownerCt.sealed)) {

                doPosition = true;
                fromPanel = from.up('tablepanel');
                toPanel = to.up('tablepanel');

                // If it's a lock operation, check that it's allowable.
                data.isLock   = toPanel.isLocked && !fromPanel.isLocked;
                data.isUnlock = !toPanel.isLocked && fromPanel.isLocked;
                if ((data.isUnlock && from.lockable === false) || (data.isLock && !from.isLockable())) {
                    doPosition = false;
                }
            }
        }

        if (doPosition) {
            me.positionIndicator(data, node, e);
        } else {
            me.valid = false;
        }
        return me.valid ? me.dropAllowed : me.dropNotAllowed;
    },

    hideIndicators: function() {
        var me = this;
        
        me.getTopIndicator().hide();
        me.getBottomIndicator().hide();
        me.lastTargetHeader = me.lastDropPos = null;

    },

    onNodeOut: function() {
        this.hideIndicators();
    },

    onNodeDrop: function(node, dragZone, e, data) {
        if (this.valid) {
            var dragHeader     = data.header,
                dropLocation   = data.dropLocation,
                targetHeader   = dropLocation.header,
                fromCt         = dragHeader.ownerCt,
                localFromIdx   = fromCt.items.indexOf(dragHeader), // Container.items is a MixedCollection
                toCt           = targetHeader.ownerCt,
                localToIdx     = toCt.items.indexOf(targetHeader),
                headerCt       = this.headerCt,
                // Use the visible column manager here, the indexes we want are for moving
                // the actual cells in the grid, which is only visible items
                columns        = headerCt.visibleColumnManager,
                fromIdx        = columns.getHeaderIndex(dragHeader),
                toIdx          = columns.getHeaderIndex(targetHeader),
                colsToMove     = dragHeader.isGroupHeader ? dragHeader.query(':not([hidden]):not([isGroupHeader])').length : 1,
                sameCt         = fromCt === toCt,
                scrollerOwner, savedWidth;

            // Drop position is to the right of the targetHeader, increment the toIdx correctly
            if (dropLocation.pos === 'after') {
                localToIdx++;
                toIdx += targetHeader.isGroupHeader ? targetHeader.query(':not([hidden]):not([isGroupHeader])').length : 1;
            }

            // If we are dragging in between two HeaderContainers that have had the lockable
            // mixin injected we will lock/unlock headers in between sections, and then continue
            // with another execution of onNodeDrop to ensure the header is dropped into the correct group
            if (data.isLock) {
                scrollerOwner = fromCt.up('[scrollerOwner]');
                scrollerOwner.lock(dragHeader, localToIdx, toCt);
            } else if (data.isUnlock) {
                scrollerOwner = fromCt.up('[scrollerOwner]');
                scrollerOwner.unlock(dragHeader, localToIdx, toCt);
            }

            // This is a drop within the same HeaderContainer.
            else {
                this.invalidateDrop();
                // Cache the width here, we need to get it before we removed it from the DOM
                savedWidth = dragHeader.getWidth();

                // Dragging within the same container.
                if (sameCt) {
                    // If dragging rightwards, then after removal, the insertion index will be less.
                    if (localToIdx > localFromIdx) {
                        localToIdx -= 1;
                    }
                    // A no-op. This can happen when cross lockable drag operations recurse (see above).
                    // If a drop was a lock/unlock, and the lock/unlock call placed the column in the
                    // desired position (lock places at end, unlock places at beginning) then we're done.
                    if (localToIdx === localFromIdx) {
                        // We still need to inform the rest of the components so that events can be fired.
                        headerCt.onHeaderMoved(dragHeader, colsToMove, fromIdx, toIdx);
                        return;
                    }
                }

                // Suspend layouts while we sort all this out.
                Ext.suspendLayouts();

                dragHeader.dropMoving = true;
                if (sameCt) {
                    toCt.move(localFromIdx, localToIdx);
                } else {
                    fromCt.remove(dragHeader, false);
                    toCt.insert(localToIdx, dragHeader);
                }
                delete dragHeader.dropMoving;

                // Group headers acquire the aggregate width of their child headers
                // Therefore a child header may not flex; it must contribute a fixed width.
                // But we restore the flex value when moving back into the main header container
                if (toCt.isGroupHeader) {
                    // Adjust the width of the "to" group header only if we dragged in from somewhere else.
                    if (!sameCt) {
                        dragHeader.savedFlex = dragHeader.flex;
                        delete dragHeader.flex;
                        dragHeader.width = savedWidth;
                    }
                } else {
                    if (dragHeader.savedFlex) {
                        dragHeader.flex = dragHeader.savedFlex;
                        delete dragHeader.width;
                    }
                }

                // Refresh columns cache in case we remove an emptied group column
                headerCt.purgeCache();
                Ext.resumeLayouts(true);
                headerCt.onHeaderMoved(dragHeader, colsToMove, fromIdx, toIdx);

                // Ext.grid.header.Container will handle the removal of empty groups, don't handle it here
            }
        }
    }
});

/**
 * @private
 */
Ext.define('Ext.grid.plugin.HeaderReorderer', {
    extend:  Ext.AbstractPlugin ,
                                                                       
    alias: 'plugin.gridheaderreorderer',

    init: function(headerCt) {
        this.headerCt = headerCt;
        headerCt.on({
            render: this.onHeaderCtRender,
            single: true,
            scope: this
        });
    },

    /**
     * @private
     * AbstractComponent calls destroy on all its plugins at destroy time.
     */
    destroy: function() {
        Ext.destroy(this.dragZone, this.dropZone);
    },

    onHeaderCtRender: function() {
        var me = this;
        
        me.dragZone = new Ext.grid.header.DragZone(me.headerCt);
        me.dropZone = new Ext.grid.header.DropZone(me.headerCt);
        if (me.disabled) {
            me.dragZone.disable();
        }
    },
    
    enable: function() {
        this.disabled = false;
        if (this.dragZone) {
            this.dragZone.enable();
        }
    },
    
    disable: function() {
        this.disabled = true;
        if (this.dragZone) {
            this.dragZone.disable();
        }
    }
});

/**
 * Container which holds headers and is docked at the top or bottom of a TablePanel.
 * The HeaderContainer drives resizing/moving/hiding of columns within the TableView.
 * As headers are hidden, moved or resized the headercontainer is responsible for
 * triggering changes within the view.
 */
Ext.define('Ext.grid.header.Container', {
    extend:  Ext.container.Container ,
               
                                
                                        
                                         
      
           
                                 
                                 
                        
                             
                            
      
    border: true,

    alias: 'widget.headercontainer',

    baseCls: Ext.baseCSSPrefix + 'grid-header-ct',
    dock: 'top',

    /**
     * @cfg {Number} weight
     * HeaderContainer overrides the default weight of 0 for all docked items to 100.
     * This is so that it has more priority over things like toolbars.
     */
    weight: 100,

    defaultType: 'gridcolumn',

    detachOnRemove: false,

    /**
     * @cfg {Number} defaultWidth
     * Width of the header if no width or flex is specified.
     */
    defaultWidth: 100,

    /**
     * @cfg {Boolean} [sealed=false]
     * Specify as `true` to constrain column dragging so that a column cannot be dragged into or out of this column.
     *
     * **Note that this config is only valid for column headers which contain child column headers, eg:**
     *     {
     *         sealed: true
     *         text: 'ExtJS',
     *         columns: [{
     *             text: '3.0.4',
     *             dataIndex: 'ext304'
     *         }, {
     *             text: '4.1.0',
     *             dataIndex: 'ext410'
     *         }
     *     }
     *
     */

    //<locale>
    sortAscText: 'Sort Ascending',
    //</locale>
    //<locale>
    sortDescText: 'Sort Descending',
    //</locale>
    //<locale>
    sortClearText: 'Clear Sort',
    //</locale>
    //<locale>
    columnsText: 'Columns',
    //</locale>

    headerOpenCls: Ext.baseCSSPrefix + 'column-header-open',

    menuSortAscCls: Ext.baseCSSPrefix + 'hmenu-sort-asc',

    menuSortDescCls: Ext.baseCSSPrefix + 'hmenu-sort-desc',

    menuColsIcon: Ext.baseCSSPrefix + 'cols-icon',

    // private; will probably be removed by 4.0
    triStateSort: false,

    ddLock: false,

    dragging: false,

    /**
     * @property {Boolean} isGroupHeader
     * True if this HeaderContainer is in fact a group header which contains sub headers.
     */

    /**
     * @cfg {Boolean} sortable
     * Provides the default sortable state for all Headers within this HeaderContainer.
     * Also turns on or off the menus in the HeaderContainer. Note that the menu is
     * shared across every header and therefore turning it off will remove the menu
     * items for every header.
     */
    sortable: true,

    /**
     * @cfg {Boolean} [enableColumnHide=true]
     * False to disable column hiding within this grid.
     */
    enableColumnHide: true,

    initComponent: function() {
        var me = this;

        me.headerCounter = 0;
        me.plugins = me.plugins || [];

        // TODO: Pass in configurations to turn on/off dynamic
        //       resizing and disable resizing all together

        // Only set up a Resizer and Reorderer for the topmost HeaderContainer.
        // Nested Group Headers are themselves HeaderContainers
        if (!me.isColumn) {
            if (me.enableColumnResize) {
                me.resizer = new Ext.grid.plugin.HeaderResizer();
                me.plugins.push(me.resizer);
            }
            if (me.enableColumnMove) {
                me.reorderer = new Ext.grid.plugin.HeaderReorderer();
                me.plugins.push(me.reorderer);
            }
        }

        // If this is a leaf column header, and is NOT functioning as a container,
        // use Container layout with a no-op calculate method.
        if (me.isColumn && !me.isGroupHeader) {
            if (!me.items || me.items.length === 0) {
                me.isContainer = false;
                me.layout = {
                    type: 'container',
                    calculate: Ext.emptyFn
                };
            }
        }

        // HeaderContainer and Group header needs a gridcolumn layout.
        else {
            me.layout = Ext.apply({
                type: 'gridcolumn',
                align: 'stretch'
            }, me.initialConfig.layout);

            // If the header isn't a column ([isColumn] or [isGroupHeader]), then it's the root header.
            if (!me.isGroupHeader) {
                me.isRootHeader = true;

                // Create column managers for the root header.
                me.columnManager = new Ext.grid.ColumnManager(false, me);
                me.visibleColumnManager = new Ext.grid.ColumnManager(true, me);

                // In the grid config, if grid.columns is a header container instance and not a columns
                // config, then it currently has no knowledge of a containing grid. Create the column
                // manager now and bind it to the grid later in Ext.panel.Table:initComponent().
                //
                // In most cases, though, grid.columns will be a config, so the grid is already known
                // and the column manager can be bound to it.
                if (me.grid) {
                    me.grid.columnManager = me.columnManager;
                    me.grid.visibleColumnManager = me.visibleColumnManager;
                }
            } else {
                // Is a group header, also create column managers.
                me.visibleColumnManager = new Ext.grid.ColumnManager(true, me);
                me.columnManager = new Ext.grid.ColumnManager(false, me);
            }
        }

        me.defaults = me.defaults || {};
        Ext.applyIf(me.defaults, {
            triStateSort: me.triStateSort,
            sortable: me.sortable
        });

        me.menuTask = new Ext.util.DelayedTask(me.updateMenuDisabledState, me);
        me.callParent();
        me.addEvents(
            /**
             * @event columnresize
             * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
             * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
             * @param {Number} width
             */
            'columnresize',

            /**
             * @event headerclick
             * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
             * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
             * @param {Ext.EventObject} e
             * @param {HTMLElement} t
             */
            'headerclick',

            /**
             * @event headercontextmenu
             * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
             * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
             * @param {Ext.EventObject} e
             * @param {HTMLElement} t
             */
            'headercontextmenu',

            /**
             * @event headertriggerclick
             * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
             * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
             * @param {Ext.EventObject} e
             * @param {HTMLElement} t
             */
            'headertriggerclick',

            /**
             * @event columnmove
             * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
             * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
             * @param {Number} fromIdx
             * @param {Number} toIdx
             */
            'columnmove',
            /**
             * @event columnhide
             * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
             * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
             */
            'columnhide',
            /**
             * @event columnshow
             * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
             * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
             */
            'columnshow',
            /**
             * @event columnschanged
             * Fired after the columns change in any way, when a column has been hidden or shown, or when a column
             * is added to or removed from this header container.
             * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
             */
            'columnschanged',
            /**
             * @event sortchange
             * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
             * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
             * @param {String} direction
             */
            'sortchange',
            /**
             * @event menucreate
             * Fired immediately after the column header menu is created.
             * @param {Ext.grid.header.Container} ct This instance
             * @param {Ext.menu.Menu} menu The Menu that was created
             */
            'menucreate'
        );
    },

    isLayoutRoot: function(){
        // Since we're docked, the width is always calculated
        // If we're hidden, the height is explicitly 0, which
        // means we'll be considered a layout root. However, we
        // still need the view to layout to update the underlying
        // table to match the size.
        if (this.hiddenHeaders) {
            return false;
        }
        return this.callParent();
    },
    
    // Find the topmost HeaderContainer
    getOwnerHeaderCt: function() {
        var me = this;
        return me.isRootHeader ? me : me.up('[isRootHeader]');
    },

    onDestroy: function() {
        var me = this;

        if (me.menu) {
            me.menu.un('hide', me.onMenuHide, me);
        }
        me.menuTask.cancel();
        me.callParent();
        Ext.destroy(me.resizer, me.reorderer, me.visibleColumnManager, me.columnManager, me.menu);
        me.columnManager = me.visibleColumnManager = null;
    },

    applyColumnsState: function(columns) {
        if (!columns || !columns.length) {
            return;
        }

        var me     = this,
            items  = me.items.items,
            count  = items.length,
            i      = 0,
            length = columns.length,
            c, col, columnState, index;

        for (c = 0; c < length; c++) {
            columnState = columns[c];

            for (index = count; index--; ) {
                col = items[index];
                if (col.getStateId && col.getStateId() == columnState.id) {
                    // If a column in the new grid matches up with a saved state...
                    // Ensure that the column is restored to the state order.
                    // i is incremented upon every column match, so all persistent
                    // columns are ordered before any new columns.
                    if (i !== index) {
                        me.moveHeader(index, i);
                    }

                    if (col.applyColumnState) {
                        col.applyColumnState(columnState);
                    }
                    ++i;
                    break;
                }
            }
        }
    },

    getColumnsState: function () {
        var me = this,
            columns = [],
            state;

        me.items.each(function (col) {
            state = col.getColumnState && col.getColumnState();
            if (state) {
                columns.push(state);
            }
        });

        return columns;
    },

    // Invalidate column cache on add
    // We cannot refresh the View on every add because this method is called
    // when the HeaderDropZone moves Headers around, that will also refresh the view
    onAdd: function(c) {
        var me = this;

        if (!c.headerId) {
            c.headerId = c.initialConfig.id || Ext.id(null, 'header-');
        }

        // Only generate a stateId if it really needs one - ie, it cannot yield a stateId
        if (!c.getStateId()) {
            // This was the headerId generated in 4.0, so to preserve saved state, we now
            // assign a default stateId in that same manner. The stateId's of a column are
            // not global at the stateProvider, but are local to the grid state data. The
            // headerId should still follow our standard naming convention.
            c.stateId = c.initialConfig.id || ('h' + (++me.headerCounter));
        }

        me.callParent(arguments);
        me.onColumnsChanged();
        me.checkMoveNotify(c);
    },

    onMove: function(c, fromIdx, toIdx) {
        var me = this;
        
        me.callParent(arguments);
        me.onColumnsChanged();
        if (!c.dropMoving) {
            // The drop code will take care of updating the view
            me.checkMoveNotify(c);
            me.fireEvent('columnmove', me, c, fromIdx, toIdx);
        }
    },
    
    // Invalidate column cache on remove
    // We cannot refresh the View on every remove because this method is called
    // when the HeaderDropZone moves Headers around, that will also refresh the view
    onRemove: function(c) {
        var me = this,
            ownerCt = me.ownerCt;

        me.callParent(arguments);


        if (!me.destroying) {
            me.onColumnsChanged();
            me.checkMoveNotify(c);
            if (me.isGroupHeader && !me.items.getCount() && ownerCt) {
                // Detach the header from the DOM here. Since we're removing and destroying the container,
                // the inner DOM may get overwritten, since Container::deatchOnRemove gets processed after
                // onRemove.
                me.detachComponent(c);
                // If we don't have any items left and we're a group, remove ourselves.
                // This will cascade up if necessary
                Ext.suspendLayouts();
                ownerCt.remove(me);
                Ext.resumeLayouts(true);
            }
        }
    },
    
    checkMoveNotify: function(c) {
        var owner, root;
        if (!c.isGroupHeader) {
            // Get the root header
            root = this.getOwnerHeaderCt();
            if (root) {
                // Reference the panel.Table
                owner = root.ownerCt;
                if (owner) {
                    owner.onHeadersChanged(root, c);
                }
            }
        }
    },

    onShow: function() {
        this.callParent(arguments);
        this.onColumnsChanged();
    },

    // Private
    // Called whenever a column is added or removed or moved.
    // Ensures that the gridColumns caches are cleared.
    onColumnsChanged: function() {
        var headerCt = this;

        // Each HeaderContainer up the chain must have its cache purged so that its getGridColumns method will return correct results.
        this.purgeHeaderCtCache(headerCt);

        if (headerCt && headerCt.rendered) {
            headerCt.fireEvent('columnschanged', headerCt);
        }
    },
    
    // @private
    applyDefaults: function(config) {
        var ret;
        /*
         * Ensure header.Container defaults don't get applied to a RowNumberer
         * if an xtype is supplied. This isn't an ideal solution however it's
         * much more likely that a RowNumberer with no options will be created,
         * wanting to use the defaults specified on the class as opposed to
         * those setup on the Container.
         */
        if (config && !config.isComponent && config.xtype == 'rownumberer') {
            ret = config;
        } else {
            ret = this.callParent(arguments);

            // Apply default width unless it's a group header (in which case it must be left to shrinkwrap), or it's flexed
            if (!config.isGroupHeader && !('width' in ret) && !ret.flex) {
                ret.width = this.defaultWidth;
            }
        }
        return ret;
    },

    setSortState: function(){
        var store   = this.up('[store]').store,
            // grab the first sorter, since there may also be groupers
            // in this collection
            first = store.getFirstSorter(),
            hd;

        if (first) {
            hd = this.down('gridcolumn[dataIndex=' + first.property  +']');
            if (hd) {
                hd.setSortState(first.direction, false, true);
            }
        } else {
            this.clearOtherSortStates(null);
        }
    },

    getHeaderMenu: function(){
        var menu = this.getMenu(),
            item;

        if (menu) {
            item = menu.child('#columnItem');
            if (item) {
                return item.menu;
            }
        }
        return null;
    },

    onHeaderVisibilityChange: function(header, visible){
        var me = this,
            menu = me.getHeaderMenu(),
            item;

        // Invalidate column collections upon column hide/show
        me.purgeHeaderCtCache(header.ownerCt);

        if (menu) {
            // If the header was hidden programmatically, sync the Menu state
            item = me.getMenuItemForHeader(menu, header);
            if (item) {
                item.setChecked(visible, true);
            }
            // delay this since the headers may fire a number of times if we're hiding/showing groups
            if (menu.isVisible()) {
                me.menuTask.delay(50);
            }
        }
    },

    updateMenuDisabledState: function(menu) {
        var me = this,
            columns = me.query(':not([hidden])'),
            i,
            len = columns.length,
            item,
            checkItem,
            method;

        // If called from menu creation, it will be passed to avoid infinite recursion
        if (!menu) {
            menu = me.getMenu();
        }

        for (i = 0; i < len; ++i) {
            item = columns[i];
            checkItem = me.getMenuItemForHeader(menu, item);
            if (checkItem) {
                method = item.isHideable() ? 'enable' : 'disable';
                if (checkItem.menu) {
                    method += 'CheckChange';
                }
                checkItem[method]();
            }
        }
    },

    getMenuItemForHeader: function(menu, header) {
        return header ? menu.down('menucheckitem[headerId=' + header.id + ']') : null;
    },

    onHeaderShow: function(header) {
        // Pass up to the GridSection
        var me = this,
            gridSection = me.ownerCt;

        if (me.forceFit) {
            delete me.flex;

        }

        me.onHeaderVisibilityChange(header, true);

        // Only update the grid UI when we are notified about base level Header shows;
        // Group header shows just cause a layout of the HeaderContainer
        if (!header.isGroupHeader) {
            if (gridSection) {
                gridSection.onHeaderShow(me, header);
            }
        }
        me.fireEvent('columnshow', me, header);
        me.fireEvent('columnschanged', this);
    },

    onHeaderHide: function(header) {
        // Pass up to the GridSection
        var me = this,
            gridSection = me.ownerCt;

        me.onHeaderVisibilityChange(header, false);

        // Only update the UI when we are notified about base level Header hides;
        if (!header.isGroupHeader) {
            if (gridSection) {
                gridSection.onHeaderHide(me, header);
            }
        }
        me.fireEvent('columnhide', me, header);
        me.fireEvent('columnschanged', this);
    },

    /**
     * Temporarily lock the headerCt. This makes it so that clicking on headers
     * don't trigger actions like sorting or opening of the header menu. This is
     * done because extraneous events may be fired on the headers after interacting
     * with a drag drop operation.
     * @private
     */
    tempLock: function() {
        this.ddLock = true;
        Ext.Function.defer(function() {
            this.ddLock = false;
        }, 200, this);
    },

    onHeaderResize: function(header, w, suppressFocus) {
        var me = this,
            view = me.view,
            gridSection = me.ownerCt;

        // Do not react to header sizing during initial Panel layout when there is no view content to size.
        if (view && view.body.dom) {
            me.tempLock();
            if (gridSection) {
                gridSection.onHeaderResize(me, header, w);
            }
        }
        me.fireEvent('columnresize', this, header, w);
    },

    onHeaderClick: function(header, e, t) {
        header.fireEvent('headerclick', this, header, e, t);
        this.fireEvent('headerclick', this, header, e, t);
    },

    onHeaderContextMenu: function(header, e, t) {
        header.fireEvent('headercontextmenu', this, header, e, t);
        this.fireEvent('headercontextmenu', this, header, e, t);
    },

    onHeaderTriggerClick: function(header, e, t) {
        // generate and cache menu, provide ability to cancel/etc
        var me = this;
        if (header.fireEvent('headertriggerclick', me, header, e, t) !== false && me.fireEvent('headertriggerclick', me, header, e, t) !== false) {
            me.showMenuBy(t, header);
        }
    },

    showMenuBy: function(t, header) {
        var menu = this.getMenu(),
            ascItem  = menu.down('#ascItem'),
            descItem = menu.down('#descItem'),
            sortableMth;

        // Use ownerButton as the upward link. Menus *must have no ownerCt* - they are global floaters.
        // Upward navigation is done using the up() method.
        menu.activeHeader = menu.ownerButton = header;
        header.setMenuActive(true);

        // enable or disable asc & desc menu items based on header being sortable
        sortableMth = header.sortable ? 'enable' : 'disable';
        if (ascItem) {
            ascItem[sortableMth]();
        }
        if (descItem) {
            descItem[sortableMth]();
        }
        menu.showBy(t);
    },

    // remove the trigger open class when the menu is hidden
    onMenuHide: function(menu) {
        menu.activeHeader.setMenuActive(false);
    },

    moveHeader: function(fromIdx, toIdx) {
        // An automatically expiring lock
        this.tempLock();
        this.onHeaderMoved(this.move(fromIdx, toIdx), 1, fromIdx, toIdx);
    },

    purgeHeaderCtCache: function (headerCt) {
        while (headerCt) {
            headerCt.purgeCache();
            if (headerCt.isRootHeader) {
                return;
            }
            headerCt = headerCt.ownerCt;
        }
    },

    purgeCache: function() {
        var me = this,
            menu = me.menu,
            visibleColumnManager = me.visibleColumnManager,
            columnManager = me.columnManager;
            
        // Delete column cache - column order has changed.
        me.gridVisibleColumns = me.gridDataColumns = me.hideableColumns = null;

        // ColumnManager. Only the top
        if (visibleColumnManager) {
            visibleColumnManager.invalidate();
            columnManager.invalidate();
        }

        // Menu changes when columns are moved. It will be recreated.
        // Menu does not change when columns are hidden or shown (which is all that happens when menu is visible)
        if (menu && menu.hidden) {
            // Must hide before destroy so that trigger el is deactivated
            menu.hide();
            menu.destroy();
            me.menu = null;
        }
    },

    onHeaderMoved: function (header, colsToMove, fromIdx, toIdx) {
        var me = this,
            gridSection = me.ownerCt;

        if (me.rendered) {
            if (gridSection && gridSection.onHeaderMove) {
                gridSection.onHeaderMove(me, header, colsToMove, fromIdx, toIdx);
            }
            me.fireEvent('columnmove', me, header, fromIdx, toIdx);
        }
    },

    /**
     * Gets the menu (and will create it if it doesn't already exist)
     * @private
     */
    getMenu: function() {
        var me = this;

        if (!me.menu) {
            me.menu = new Ext.menu.Menu({
                hideOnParentHide: false,  // Persists when owning ColumnHeader is hidden
                items: me.getMenuItems(),
                listeners: {
                    hide: me.onMenuHide,
                    scope: me
                }
            });
            me.fireEvent('menucreate', me, me.menu);
        }
        me.updateMenuDisabledState(me.menu);
        return me.menu;
    },

    /**
     * Returns an array of menu items to be placed into the shared menu
     * across all headers in this header container.
     * @returns {Array} menuItems
     */
    getMenuItems: function() {
        var me = this,
            menuItems = [],
            hideableColumns = me.enableColumnHide ? me.getColumnMenu(me) : null;

        if (me.sortable) {
            menuItems = [{
                itemId: 'ascItem',
                text: me.sortAscText,
                iconCls: me.menuSortAscCls,
                handler: me.onSortAscClick,
                scope: me
            },{
                itemId: 'descItem',
                text: me.sortDescText,
                iconCls: me.menuSortDescCls,
                handler: me.onSortDescClick,
                scope: me
            }];
        }
        if (hideableColumns && hideableColumns.length) {
            if (me.sortable) {
                menuItems.push('-');
            }
            menuItems.push({
                itemId: 'columnItem',
                text: me.columnsText,
                iconCls: me.menuColsIcon,
                menu: hideableColumns,
                hideOnClick: false
            });
        }
        return menuItems;
    },

    // sort asc when clicking on item in menu
    onSortAscClick: function() {
        var menu = this.getMenu(),
            activeHeader = menu.activeHeader;

        activeHeader.setSortState('ASC');
    },

    // sort desc when clicking on item in menu
    onSortDescClick: function() {
        var menu = this.getMenu(),
            activeHeader = menu.activeHeader;

        activeHeader.setSortState('DESC');
    },

    /**
     * Returns an array of menu CheckItems corresponding to all immediate children
     * of the passed Container which have been configured as hideable.
     */
    getColumnMenu: function(headerContainer) {
        var menuItems = [],
            i = 0,
            item,
            items = headerContainer.query('>gridcolumn[hideable]'),
            itemsLn = items.length,
            menuItem;

        for (; i < itemsLn; i++) {
            item = items[i];
            menuItem = new Ext.menu.CheckItem({
                text: item.menuText || item.text,
                checked: !item.hidden,
                hideOnClick: false,
                headerId: item.id,
                menu: item.isGroupHeader ? this.getColumnMenu(item) : undefined,
                checkHandler: this.onColumnCheckChange,
                scope: this
            });
            menuItems.push(menuItem);

            // If the header is ever destroyed - for instance by dragging out the last remaining sub header,
            // then the associated menu item must also be destroyed.
            item.on({
                destroy: Ext.Function.bind(menuItem.destroy, menuItem)
            });
        }
        return menuItems;
    },

    onColumnCheckChange: function(checkItem, checked) {
        var header = Ext.getCmp(checkItem.headerId);
        header[checked ? 'show' : 'hide']();
    },

    /**
     * Returns the number of <b>grid columns</b> descended from this HeaderContainer.
     * Group Columns are HeaderContainers. All grid columns are returned, including hidden ones.
     */
    getColumnCount: function() {
        return this.getGridColumns().length;
    },

    /**
     * Gets the full width of all columns that are visible.
     */
    getFullWidth: function() {
        var fullWidth = 0,
            headers = this.getVisibleGridColumns(),
            headersLn = headers.length,
            i = 0,
            header;
           

        for (; i < headersLn; i++) {
            header = headers[i];
            // use headers getDesiredWidth if its there
            if (header.getDesiredWidth) {
                fullWidth += header.getDesiredWidth() || 0;
            // if injected a diff cmp use getWidth
            } else {
                fullWidth += header.getWidth();
            }
        }
        return fullWidth;
    },

    // invoked internally by a header when not using triStateSorting
    clearOtherSortStates: function(activeHeader) {
        var headers   = this.getGridColumns(),
            headersLn = headers.length,
            i         = 0;

        for (; i < headersLn; i++) {
            if (headers[i] !== activeHeader) {
                // unset the sortstate and dont recurse
                headers[i].setSortState(null, true);
            }
        }
    },

    /**
     * Returns an array of the **visible** columns in the grid. This goes down to the lowest column header
     * level, and does not return **grouped** headers which contain sub headers.
     * @returns {Array}
     */
    getVisibleGridColumns: function() {
        if (this.gridVisibleColumns) {
            return this.gridVisibleColumns;
        }
        
        var allColumns = this.getGridColumns(),
            result = [],
            len = allColumns.length, i;

        // Use an inline check instead of ComponentQuery filtering for better performance for
        // repeated grid row rendering - as in buffered rendering.
        for (i = 0; i < len; i++) {
            if (!allColumns[i].hidden) {
                result[result.length] = allColumns[i];
            }
        }
        this.gridVisibleColumns = result;
        return result;
    }, 

    /**
     * Returns an array of all columns which appear in the grid's View. This goes down to the leaf column header
     * level, and does not return **grouped** headers which contain sub headers.
     *
     * It includes hidden headers even though they are not rendered. This is for collection of menu items for the column hide/show menu.
     *
     * Headers which have a hidden ancestor have a `hiddenAncestor: true` property injected so that they can also be rendered at zero width without interrogating
     * that header's ownerCt axis for a hidden ancestor.
     * @returns {Array}
     */
    getGridColumns: function(/* private - used in recursion*/inResult, hiddenAncestor) {
        if (!inResult && this.gridDataColumns) {
            return this.gridDataColumns;
        }

        var me = this,
            result = inResult || [],
            items, i, len, item,
            lastVisibleColumn;

        hiddenAncestor = hiddenAncestor || me.hidden;
        if (me.items) {
            items = me.items.items;
            for (i = 0, len = items.length; i < len; i++) {
                item = items[i];
                if (item.isGroupHeader) {
                    item.getGridColumns(result, hiddenAncestor);
                } else {
                    item.hiddenAncestor = hiddenAncestor;
                    result.push(item);
                }
            }
        }
        if (!inResult) {
            me.gridDataColumns = result;
        }

        // If top level, correct first and last visible column flags
        if (!inResult && len) {
            // Set firstVisible and lastVisible flags
            for (i = 0, len = result.length; i < len; i++) {
                item = result[i];
                item.isFirstVisible = item.isLastVisible = false;
                if (!(item.hidden || item.hiddenAncestor)) {
                    if (!lastVisibleColumn) {
                        item.isFirstVisible = true;
                    }
                    lastVisibleColumn = item;
                }
            }
            // If we haven't hidden all columns, tag the last visible one encountered
            if (lastVisibleColumn) {
                lastVisibleColumn.isLastVisible = true;
            }
        }

        return result;
    },

    /**
     * @private
     * For use by column headers in determining whether there are any hideable columns when deciding whether or not
     * the header menu should be disabled.
     */
    getHideableColumns: function() {
        var me = this,
            result = me.hideableColumns;

        if (!result) {
            result = me.hideableColumns = me.query('[hideable]');
        }
        return result;
    },

    /**
     * Returns the index of a leaf level header regardless of what the nesting
     * structure is.
     *
     * If a group header is passed, the index of the first leaf level header within it is returned.
     *
     * @param {Ext.grid.column.Column} header The header to find the index of
     * @return {Number} The index of the specified column header
     */
    getHeaderIndex: function (header) {
        // Binding the columnManager to a column makes it backwards-compatible with versions
        // that only bind the columnManager to a root header.
        if (!this.columnManager) {
            this.columnManager = this.getOwnerHeaderCt().columnManager;
        }

        return this.columnManager.getHeaderIndex(header);
    },

    /**
     * Get a leaf level header by index regardless of what the nesting
     * structure is.
     *
     * @param {Number} index The column index for which to retrieve the column.
     */
    getHeaderAtIndex: function (index) {
        // Binding the columnManager to a column makes it backwards-compatible with versions
        // that only bind the columnManager to a root header.
        if (!this.columnManager) {
            this.columnManager = this.getOwnerHeaderCt().columnManager;
        }

        return this.columnManager.getHeaderAtIndex(index);
    },

    /**
     * When passed a column index, returns the closet *visible* column to that. If the column at the passed index is visible,
     * that is returned. If it is hidden, either the next visible, or the previous visible column is returned.
     *
     * @param {Number} index Position at which to find the closest visible column.
     */
    getVisibleHeaderClosestToIndex: function (index) {
        // Binding the columnManager to a column makes it backwards-compatible with versions
        // that only bind the columnManager to a root header.
        if (!this.visibleColumnManager) {
            this.visibleColumnManager = this.getOwnerHeaderCt().visibleColumnManager;
        }

        return this.visibleColumnManager.getVisibleHeaderClosestToIndex(index);
    },

    applyForceFit: function (header) {
        var me = this,
            view = me.view,
            minWidth = Ext.grid.plugin.HeaderResizer.prototype.minColWidth,
            // Used when a column's max contents are larger than the available view width.
            useMinWidthForFlex = false,
            defaultWidth = Ext.grid.header.Container.prototype.defaultWidth,
            availFlex = me.el.getViewSize().width - (view.el.dom.scrollHeight > view.el.dom.clientHeight ? Ext.getScrollbarSize().width : 0),
            totalFlex = 0,
            items = me.getVisibleGridColumns(),
            hidden = header.hidden,
            len, i,
            item,
            maxAvailFlexOneColumn,
            myWidth;

        function getTotalFlex() {
            for (i = 0, len = items.length; i < len; i++) {
                item = items[i];

                // Skip the current header.
                if (item === header) {
                    continue;
                }

                item.flex = item.flex || item.width || item.getWidth();
                totalFlex += item.flex;
                item.width = null;
            }
        }

        function applyWidth() {
            // The currently-sized column (whether resized or reshown) will already
            // have a width, so all other columns will need to be flexed.
            var isCurrentHeader;

            for (i = 0, len = items.length; i < len; i++) {
                item = items[i];
                isCurrentHeader = (item === header);

                if (useMinWidthForFlex && !isCurrentHeader) {
                    // The selected column is extremely large so set all the others as flex minWidth.
                    item.flex = minWidth;
                    item.width = null;
                } else if (!isCurrentHeader) {
                    // Note that any widths MUST be converted to flex. Imagine that all but one columns
                    // are hidden.  The widths of each column very easily could be greater than the total
                    // available width (think about the how visible header widths increase as sibling
                    // columns are hidden), so they cannot be reliably used to size the header, and the only
                    // safe approach is to convert any all widths to flex (except for the current header).
                    myWidth = item.flex || defaultWidth;
                    item.flex = Math.max(Math.ceil((myWidth / totalFlex) * availFlex), minWidth);
                    item.width = null;
                }

                item.setWidth(item.width || item.flex);
            }
        }

        Ext.suspendLayouts();

        // Determine the max amount of flex that a single column can have.
        maxAvailFlexOneColumn = (availFlex - ((items.length + 1) * minWidth));

        // First, remove the header's flex as it should always receive a set width
        // since it is the header being operated on.
        header.flex = null;

        if (hidden) {
            myWidth = header.width || header.savedWidth;
            header.savedWidth = null;
        } else {
            myWidth = view.getMaxContentWidth(header);
        }

        // We need to know if the max content width of the selected column would blow out the
        // grid. If so, all the other visible columns will be flexed to minWidth.
        if (myWidth >  maxAvailFlexOneColumn) {
            header.width = maxAvailFlexOneColumn;
            useMinWidthForFlex = true;
        } else {
            header.width = myWidth;

            // Substract the current header's width from the available flex + some padding
            // to ensure that the last column doesn't get nudged out of the view.
            availFlex -= myWidth + defaultWidth;
            getTotalFlex();
        }

        applyWidth();

        Ext.resumeLayouts(true);
    },

    autoSizeColumn: function (header) {
        var view = this.view;

        if (view) {
            view.autoSizeColumn(header);
            if (this.forceFit) {
                this.applyForceFit(header);
            }
        }
    }
});

/**
 * This class specifies the definition for a column inside a {@link Ext.grid.Panel}. It encompasses
 * both the grid header configuration as well as displaying data within the grid itself. If the
 * {@link #columns} configuration is specified, this column will become a column group and can
 * contain other columns inside. In general, this class will not be created directly, rather
 * an array of column configurations will be passed to the grid:
 *
 *     @example
 *     Ext.create('Ext.data.Store', {
 *         storeId:'employeeStore',
 *         fields:['firstname', 'lastname', 'seniority', 'dep', 'hired'],
 *         data:[
 *             {firstname:"Michael", lastname:"Scott", seniority:7, dep:"Management", hired:"01/10/2004"},
 *             {firstname:"Dwight", lastname:"Schrute", seniority:2, dep:"Sales", hired:"04/01/2004"},
 *             {firstname:"Jim", lastname:"Halpert", seniority:3, dep:"Sales", hired:"02/22/2006"},
 *             {firstname:"Kevin", lastname:"Malone", seniority:4, dep:"Accounting", hired:"06/10/2007"},
 *             {firstname:"Angela", lastname:"Martin", seniority:5, dep:"Accounting", hired:"10/21/2008"}
 *         ]
 *     });
 *
 *     Ext.create('Ext.grid.Panel', {
 *         title: 'Column Demo',
 *         store: Ext.data.StoreManager.lookup('employeeStore'),
 *         columns: [
 *             {text: 'First Name',  dataIndex:'firstname'},
 *             {text: 'Last Name',  dataIndex:'lastname'},
 *             {text: 'Hired Month',  dataIndex:'hired', xtype:'datecolumn', format:'M'},
 *             {text: 'Department (Yrs)', xtype:'templatecolumn', tpl:'{dep} ({seniority})'}
 *         ],
 *         width: 400,
 *         forceFit: true,
 *         renderTo: Ext.getBody()
 *     });
 *
 * # Convenience Subclasses
 *
 * There are several column subclasses that provide default rendering for various data types
 *
 *  - {@link Ext.grid.column.Action}: Renders icons that can respond to click events inline
 *  - {@link Ext.grid.column.Boolean}: Renders for boolean values
 *  - {@link Ext.grid.column.Date}: Renders for date values
 *  - {@link Ext.grid.column.Number}: Renders for numeric values
 *  - {@link Ext.grid.column.Template}: Renders a value using an {@link Ext.XTemplate} using the record data
 *
 * # Setting Sizes
 *
 * The columns are laid out by a {@link Ext.layout.container.HBox} layout, so a column can either
 * be given an explicit width value or a flex configuration. If no width is specified the grid will
 * automatically the size the column to 100px. For column groups, the size is calculated by measuring
 * the width of the child columns, so a width option should not be specified in that case.
 *
 * # Header Options
 *
 *  - {@link #text}: Sets the header text for the column
 *  - {@link #sortable}: Specifies whether the column can be sorted by clicking the header or using the column menu
 *  - {@link #hideable}: Specifies whether the column can be hidden using the column menu
 *  - {@link #menuDisabled}: Disables the column header menu
 *  - {@link #cfg-draggable}: Specifies whether the column header can be reordered by dragging
 *  - {@link #groupable}: Specifies whether the grid can be grouped by the column dataIndex. See also {@link Ext.grid.feature.Grouping}
 *
 * # Data Options
 *
 *  - {@link #dataIndex}: The dataIndex is the field in the underlying {@link Ext.data.Store} to use as the value for the column.
 *  - {@link #renderer}: Allows the underlying store value to be transformed before being displayed in the grid
 */
Ext.define('Ext.grid.column.Column', {
    extend:  Ext.grid.header.Container ,
    alias: 'widget.gridcolumn',
                                                                                             
    alternateClassName: 'Ext.grid.Column',

    baseCls: Ext.baseCSSPrefix + 'column-header',

    // Not the standard, automatically applied overCls because we must filter out overs of child headers.
    hoverCls: Ext.baseCSSPrefix + 'column-header-over',

    handleWidth: 4,
    
    ariaRole: 'columnheader',

    sortState: null,

    possibleSortStates: ['ASC', 'DESC'],

    childEls: [
        'titleEl', 'triggerEl', 'textEl'
    ],
    
    /**
     * @private
     * @cfg {Boolean} [noWrap=true]
     * The default setting indicates that external CSS rules dictate that the title is `white-space: nowrap` and
     * therefore, width cannot affect the measured height by causing text wrapping. This is what the Sencha-supplied
     * styles set. If you change those styles to allow text wrapping, you must set this to `false`.
     */
    noWrap: true,

    renderTpl: [
        '<div id="{id}-titleEl" role="presentation" {tipMarkup}class="', Ext.baseCSSPrefix, 'column-header-inner',
            '<tpl if="empty"> ', Ext.baseCSSPrefix, 'column-header-inner-empty</tpl>">',
            '<span id="{id}-textEl" class="', Ext.baseCSSPrefix, 'column-header-text',
                '{childElCls}">',
                '{text}',
            '</span>',
            '<tpl if="!menuDisabled">',
                '<div id="{id}-triggerEl" role="presentation" class="', Ext.baseCSSPrefix, 'column-header-trigger',
                '{childElCls}"></div>',
            '</tpl>',
        '</div>',
        '{%this.renderContainer(out,values)%}'
    ],

    /**
     * @cfg {Object[]} columns
     * An optional array of sub-column definitions. This column becomes a group, and houses the columns defined in the
     * `columns` config.
     *
     * Group columns may not be sortable. But they may be hideable and moveable. And you may move headers into and out
     * of a group. Note that if all sub columns are dragged out of a group, the group is destroyed.
     */

    /**
     * @cfg {String} stateId
     * An identifier which identifies this column uniquely within the owning grid's {@link #stateful state}.
     *
     * This does not have to be *globally* unique. A column's state is not saved standalone. It is encapsulated within
     * the owning grid's state.
     */

    /**
     * @cfg {String} dataIndex
     * The name of the field in the grid's {@link Ext.data.Store}'s {@link Ext.data.Model} definition from
     * which to draw the column's value. **Required.**
     */
    dataIndex: null,

    /**
     * @cfg {String} text
     * The header text to be used as innerHTML (html tags are accepted) to display in the Grid.
     * **Note**: to have a clickable header with no text displayed you can use the default of `&#160;` aka `&nbsp;`.
     */
    text: '&#160;',

    /**
     * @cfg {String} header
     * The header text.
     * @deprecated 4.0 Use {@link #text} instead.
     */

    /**
     * @cfg {String} menuText
     * The text to render in the column visibility selection menu for this column.  If not
     * specified, will default to the text value.
     */
    menuText: null,

    /**
     * @cfg {String} [emptyCellText=undefined]
     * The text to diplay in empty cells (cells with a value of `undefined`, `null`, or `''`).
     *
     * Defaults to `&#160;` aka `&nbsp;`.
     */
    emptyCellText: '&#160;',

    /**
     * @cfg {Boolean} sortable
     * False to disable sorting of this column. Whether local/remote sorting is used is specified in
     * `{@link Ext.data.Store#remoteSort}`.
     */
    sortable: true,

    /**
     * @cfg {Boolean} lockable
     * If the grid is configured with {@link Ext.panel.Table#enableLocking enableLocking}, or has columns which are
     * configured with a {@link #locked} value, this option may be used to disable user-driven locking or unlocking
     * of this column. This column will remain in the side into which its own {@link #locked} configuration placed it.
     */

    /**
     * @cfg {Boolean} groupable
     * If the grid uses a {@link Ext.grid.feature.Grouping}, this option may be used to disable the header menu
     * item to group by the column selected. By default, the header menu group option is enabled. Set to false to
     * disable (but still show) the group option in the header menu for the column.
     */

    /**
     * @cfg {Boolean} fixed
     * True to prevent the column from being resizable.
     * @deprecated 4.0 Use {@link #resizable} instead.
     */

    /**
     * @cfg {Boolean} [locked=false]
     * True to lock this column in place.  Implicitly enables locking on the grid.
     * See also {@link Ext.grid.Panel#enableLocking}.
     */

    /**
     * @cfg {Boolean} resizable
     * False to prevent the column from being resizable.
     */
    resizable: true,

    /**
     * @cfg {Boolean} hideable
     * False to prevent the user from hiding this column.
     */
    hideable: true,

    /**
     * @cfg {Boolean} menuDisabled
     * True to disable the column header menu containing sort/hide options.
     */
    menuDisabled: false,

    /**
     * @cfg {Function/String} renderer
     * A renderer is an 'interceptor' method which can be used to transform data (value, appearance, etc.)
     * before it is rendered. Example:
     *
     *     {
     *         renderer: function(value){
     *             if (value === 1) {
     *                 return '1 person';
     *             }
     *             return value + ' people';
     *         }
     *     }
     *
     * Additionally a string naming an {@link Ext.util.Format} method can be passed:
     *
     *     {
     *         renderer: 'uppercase'
     *     }
     *
     * @cfg {Object} renderer.value The data value for the current cell
     * @cfg {Object} renderer.metaData A collection of metadata about the current cell; can be used or modified
     * by the renderer. Recognized properties are: tdCls, tdAttr, and style.
     * @cfg {Ext.data.Model} renderer.record The record for the current row
     * @cfg {Number} renderer.rowIndex The index of the current row
     * @cfg {Number} renderer.colIndex The index of the current column
     * @cfg {Ext.data.Store} renderer.store The data store
     * @cfg {Ext.view.View} renderer.view The current view
     * @cfg {String} renderer.return The HTML string to be rendered.
     */
    renderer: false,

    /**
     * @cfg {Object} scope
     * The scope to use when calling the {@link #renderer} function.
     */

    /**
     * @method defaultRenderer
     * When defined this will take precedence over the {@link Ext.grid.column.Column#renderer renderer} config.
     * This is meant to be defined in subclasses that wish to supply their own renderer.
     * @protected
     * @template
     */

    /**
     * @cfg {Function} editRenderer
     * A renderer to be used in conjunction with {@link Ext.grid.plugin.RowEditing RowEditing}. This renderer is used to
     * display a custom value for non-editable fields.
     */
    editRenderer: false,

    /**
     * @cfg {String} align
     * Sets the alignment of the header and rendered columns.
     * Possible values are: `'left'`, `'center'`, and `'right'`.
     */
    align: 'left',

    /**
     * @cfg {Boolean} draggable
     * False to disable drag-drop reordering of this column.
     */
    draggable: true,

    /**
     * @cfg {String} tooltip
     * A tooltip to display for this column header
     */

    /**
     * @cfg {String} [tooltipType="qtip"]
     * The type of {@link #tooltip} to use. Either 'qtip' for QuickTips or 'title' for title attribute.
     */
    tooltipType: 'qtip',

    // Header does not use the typical ComponentDraggable class and therefore we
    // override this with an emptyFn. It is controlled at the HeaderDragZone.
    initDraggable: Ext.emptyFn,

    /**
     * @cfg {String} tdCls
     * A CSS class names to apply to the table cells for this column.
     */
    tdCls: '',

    /**
     * @cfg {Object/String} editor
     * An optional xtype or config object for a {@link Ext.form.field.Field Field} to use for editing.
     * Only applicable if the grid is using an {@link Ext.grid.plugin.Editing Editing} plugin.
     */

    /**
     * @cfg {Object/String} field
     * Alias for {@link #editor}.
     * @deprecated 4.0.5 Use {@link #editor} instead.
     */

    /**
     * @property {Ext.Element} triggerEl
     * Element that acts as button for column header dropdown menu.
     */

    /**
     * @property {Ext.Element} textEl
     * Element that contains the text in column header.
     */

    /**
     * @property {Boolean} isHeader
     * @deprecated see isColumn
     * Set in this class to identify, at runtime, instances which are not instances of the
     * HeaderContainer base class, but are in fact, the subclass: Header.
     */
    isHeader: true,

    /**
     * @property {Boolean} isColumn
     * @readonly
     * Set in this class to identify, at runtime, instances which are not instances of the
     * HeaderContainer base class, but are in fact simple column headers.
     */
    isColumn: true,

    ascSortCls: Ext.baseCSSPrefix + 'column-header-sort-ASC',
    descSortCls: Ext.baseCSSPrefix + 'column-header-sort-DESC',

    componentLayout: 'columncomponent',

    groupSubHeaderCls: Ext.baseCSSPrefix + 'group-sub-header',

    groupHeaderCls: Ext.baseCSSPrefix + 'group-header',

    clickTargetName: 'titleEl',

    // So that when removing from group headers which are then empty and then get destroyed, there's no child DOM left
    detachOnRemove : true,

    // We need to override the default component resizable behaviour here
    initResizable: Ext.emptyFn,

    initComponent: function() {
        var me = this,
            renderer,
            listeners;

        if (me.header != null) {
            me.text = me.header;
            me.header = null;
        }

        if (!me.triStateSort) {
            me.possibleSortStates.length = 2;
        }

        // A group header; It contains items which are themselves Headers
        if (me.columns != null) {
            me.isGroupHeader = true;


            // The headers become child items
            me.items = me.columns;
            me.columns = me.flex = me.width = null;
            me.cls = (me.cls||'') + ' ' + me.groupHeaderCls;

            // A group cannot be sorted, or resized - it shrinkwraps its children
            me.sortable = me.resizable = false;
            me.align = 'center';
        } else {
            // Flexed Headers need to have a minWidth defined so that they can never be squeezed out of existence by the
            // HeaderContainer's specialized Box layout, the ColumnLayout. The ColumnLayout's overridden calculateChildboxes
            // method extends the available layout space to accommodate the "desiredWidth" of all the columns.
            if (me.flex) {
                me.minWidth = me.minWidth || Ext.grid.plugin.HeaderResizer.prototype.minColWidth;
            }
        }
        me.addCls(Ext.baseCSSPrefix + 'column-header-align-' + me.align);

        renderer = me.renderer;
        if (renderer) {
            // When specifying a renderer as a string, it always resolves
            // to Ext.util.Format
            if (typeof renderer == 'string') {
                me.renderer = Ext.util.Format[renderer];
            }
            me.hasCustomRenderer = true;
        } else if (me.defaultRenderer) {
            me.scope = me;
            me.renderer = me.defaultRenderer;
        }

        // Initialize as a HeaderContainer
        me.callParent(arguments);

        listeners = {
            element:        me.clickTargetName,
            click:          me.onTitleElClick,
            contextmenu:    me.onTitleElContextMenu,
            mouseenter:     me.onTitleMouseOver,
            mouseleave:     me.onTitleMouseOut,
            scope:          me
        };
        if (me.resizable) {
            listeners.dblclick = me.onTitleElDblClick;
        }
        me.on(listeners);
    },
    
    initItems: function() {
        var me = this;
        
        me.callParent(arguments);
        
        if (me.isGroupHeader) {
            if (!me.hasVisibleChildren()) {
                me.hide();
            }
        }
    },
    
    hasVisibleChildren: function() {
        var items = this.items.items,
            len = items.length,
            i;
            
        for (i = 0; i < len; ++i) {
            if (!items[i].hidden) {
                return true;
            }
        }   
        return false; 
    },

    onAdd: function(child) {
        var me = this,
            ownerHeaderCt = me.getOwnerHeaderCt();
            
        if (child.isColumn) {
            child.isSubHeader = true;
            child.addCls(this.groupSubHeaderCls);
        }
        
        if (me.hidden) {
            // Only hide automatically during construction time (no header)
            if (!ownerHeaderCt) {
                child.hide();
            } else if (!child.hidden) {
                me.show();
            }
        }
        me.callParent(arguments);
    },

    onRemove: function(child) {
        var me = this;
        
        if (child.isSubHeader) {
            child.isSubHeader = false;
            child.removeCls(me.groupSubHeaderCls);
        }
        me.callParent(arguments);
        
        // By this point, the component will be removed from the items collection
        if (me.isGroupHeader && !me.hasVisibleChildren()) {
            me.hide();
        }
    },

    initRenderData: function() {
        var me = this,
            tipMarkup = '',
            tip = me.tooltip,
            text = me.text,
            attr = me.tooltipType == 'qtip' ? 'data-qtip' : 'title';

        if (!Ext.isEmpty(tip)) {
            tipMarkup = attr + '="' + tip + '" ';
        }

        return Ext.applyIf(me.callParent(arguments), {
            text: text,
            empty: text === '&#160;' || text === ' ' || text === '',
            menuDisabled: me.menuDisabled,
            tipMarkup: tipMarkup
        });
    },

    applyColumnState: function (state) {
        var me = this;
 
        // apply any columns
        me.applyColumnsState(state.columns);

        // Only state properties which were saved should be restored.
        // (Only user-changed properties were saved by getState)
        if (state.hidden != null) {
            me.hidden = state.hidden;
        }
        if (state.locked != null) {
            me.locked = state.locked;
        }
        if (state.sortable != null) {
            me.sortable = state.sortable;
        }
        if (state.width != null) {
            me.flex = null;
            me.width = state.width;
        } else if (state.flex != null) {
            me.width = null;
            me.flex = state.flex;
        }
    },

    getColumnState: function () {
        var me = this,
            items = me.items.items,
            // Check for the existence of items, since column.Action won't have them
            iLen = items ? items.length : 0,
            i,
            columns = [],
            state = {
                id: me.getStateId()
            };

        me.savePropsToState(['hidden', 'sortable', 'locked', 'flex', 'width'], state);
 
        if (me.isGroupHeader) {
            for (i = 0; i < iLen; i++) {
                columns.push(items[i].getColumnState());
            }

            if (columns.length) {
                state.columns = columns;
            }
        } else if (me.isSubHeader && me.ownerCt.hidden) {
            // don't set hidden on the children so they can auto height
            delete me.hidden;
        }

        if ('width' in state) {
            delete state.flex; // width wins
        }
        return state;
    },

    getStateId: function () {
        return this.stateId || this.headerId;
    },

    /**
     * Sets the header text for this Column.
     * @param {String} text The header to display on this Column.
     */
    setText: function(text) {
        this.text = text;
        if (this.rendered) {
            this.textEl.update(text);
        }
    },

    /**
     * Returns the index of this column only if this column is a base level Column. If it
     * is a group column, it returns `false`.
     * @return {Number}
     */
    getIndex: function() {
        return this.isGroupColumn ? false : this.getOwnerHeaderCt().getHeaderIndex(this);
    },

    /**
     * Returns the index of this column in the list of *visible* columns only if this column is a base level Column. If it
     * is a group column, it returns `false`.
     * @return {Number}
     */
    getVisibleIndex: function() {
        return this.isGroupColumn ? false : Ext.Array.indexOf(this.getOwnerHeaderCt().getVisibleGridColumns(), this);
    },

    beforeRender: function() {
        var me = this,
            grid = me.up('tablepanel');

        me.callParent();

        // Disable the menu if there's nothing to show in the menu, ie:
        // Column cannot be sorted, grouped or locked, and there are no grid columns which may be hidden
        if (grid && (!me.sortable || grid.sortableColumns === false) && !me.groupable &&
                     !me.lockable && (grid.enableColumnHide === false ||
                     !me.getOwnerHeaderCt().getHideableColumns().length)) {
            me.menuDisabled = true;
        }

        me.protoEl.unselectable();
    },

    afterRender: function() {
        var me = this,
            triggerEl = me.triggerEl;

        me.callParent(arguments);

        // BrowserBug: Ie8 Strict Mode, this will break the focus for this browser,
        // must be fixed when focus management will be implemented.
        if (!Ext.isIE8 || !Ext.isStrict) {
            me.mon(me.getFocusEl(), {
                focus: me.onTitleMouseOver,
                blur: me.onTitleMouseOut,
                scope: me
            });
        }
        
        if (triggerEl && me.self.triggerElWidth === undefined) {
            triggerEl.setStyle('display', 'block');
            me.self.triggerElWidth = triggerEl.getWidth();
            triggerEl.setStyle('display', '');
        }

        me.keyNav = new Ext.util.KeyNav(me.el, {
            enter: me.onEnterKey,
            down: me.onDownKey,
            scope: me
        });
    },

    // private
    // Inform the header container about the resize
    afterComponentLayout: function(width, height, oldWidth, oldHeight) {
        var me = this,
            ownerHeaderCt = me.getOwnerHeaderCt();

        me.callParent(arguments);

        if (ownerHeaderCt && (oldWidth != null || me.flex) && width !== oldWidth) {
            ownerHeaderCt.onHeaderResize(me, width, true);
        }
    },

    onDestroy: function() {
        var me = this;
        // force destroy on the textEl, IE reports a leak
        Ext.destroy(me.textEl, me.keyNav, me.field);
        me.keyNav = null;
        me.callParent(arguments);
    },

    onTitleMouseOver: function() {
        this.titleEl.addCls(this.hoverCls);
    },

    onTitleMouseOut: function() {
        this.titleEl.removeCls(this.hoverCls);
    },

    onDownKey: function(e) {
        if (this.triggerEl) {
            this.onTitleElClick(e, this.triggerEl.dom || this.el.dom);
        }
    },

    onEnterKey: function(e) {
        this.onTitleElClick(e, this.el.dom);
    },

    /**
     * @private
     * Double click handler which, if on left or right edges, auto-sizes the column to the left.
     * @param e The dblclick event
     */
    onTitleElDblClick: function(e, t) {
        var me = this,
            prev,
            leafColumns;

        // On left edge, resize previous *leaf* column in the grid
        if (me.isOnLeftEdge(e)) {

            // Look for the previous visible column header which is a leaf
            // Note: previousNode can walk out of the container (this may be first child of a group)
            prev = me.previousNode('gridcolumn:not([hidden]):not([isGroupHeader])');

            // If found in the same grid, autosize it
            if (prev && prev.getOwnerHeaderCt() === me.getOwnerHeaderCt()) {
                prev.autoSize();
            }
        }
        // On right edge, resize this column, or last sub-column within it
        else if (me.isOnRightEdge(e)) {

            // Click on right but in child container - autosize last leaf column
            if (me.isGroupHeader && e.getPoint().isContainedBy(me.layout.innerCt)) {
                leafColumns = me.query('gridcolumn:not([hidden]):not([isGroupHeader])');
                this.getOwnerHeaderCt().autoSizeColumn(leafColumns[leafColumns.length - 1]);
                return;
            }
            me.autoSize();
        }
    },

    /**
     * Sizes this Column to fit the max content width.
     * *Note that group columns shrinkwrap around the size of leaf columns. Auto sizing a group column
     * autosizes descendant leaf columns.*
     * @param {Ext.grid.column.Column/Number} The header (or index of header) to auto size.
     */
    autoSize: function() {
        var me = this,
            leafColumns,
            numLeaves, i,
            headerCt;

        // Group headers are shrinkwrap width, so autosizing one means autosizing leaf descendants.
        if (me.isGroupHeader) {
            leafColumns = me.query('gridcolumn:not([hidden]):not([isGroupHeader])');
            numLeaves = leafColumns.length;
            headerCt = this.getOwnerHeaderCt();
            Ext.suspendLayouts();
            for (i = 0; i < numLeaves; i++) {
                headerCt.autoSizeColumn(leafColumns[i]);
            }
            Ext.resumeLayouts(true);
            return;
        }
        this.getOwnerHeaderCt().autoSizeColumn(this);
    },

    onTitleElClick: function(e, t) {

        // The grid's docked HeaderContainer.
        var me = this,
            ownerHeaderCt = me.getOwnerHeaderCt();

        if (ownerHeaderCt && !ownerHeaderCt.ddLock) {
            // Firefox doesn't check the current target in a within check.
            // Therefore we check the target directly and then within (ancestors)
            if (me.triggerEl && (e.target === me.triggerEl.dom || t === me.triggerEl.dom || e.within(me.triggerEl))) {
                ownerHeaderCt.onHeaderTriggerClick(me, e, t);
            // if its not on the left hand edge, sort
            } else if (e.getKey() || (!me.isOnLeftEdge(e) && !me.isOnRightEdge(e))) {
                me.toggleSortState();
                ownerHeaderCt.onHeaderClick(me, e, t);
            }
        }
    },

    onTitleElContextMenu: function(e, t) {
        // The grid's docked HeaderContainer.
        var me = this,
            ownerHeaderCt = me.getOwnerHeaderCt();

        if (ownerHeaderCt && !ownerHeaderCt.ddLock) {
            ownerHeaderCt.onHeaderContextMenu(me, e, t);
        }
    },

    /**
     * @private
     * Process UI events from the view. The owning TablePanel calls this method, relaying events from the TableView
     * @param {String} type Event type, eg 'click'
     * @param {Ext.view.Table} view TableView Component
     * @param {HTMLElement} cell Cell HtmlElement the event took place within
     * @param {Number} recordIndex Index of the associated Store Model (-1 if none)
     * @param {Number} cellIndex Cell index within the row
     * @param {Ext.EventObject} e Original event
     */
    processEvent: function(type, view, cell, recordIndex, cellIndex, e) {
        return this.fireEvent.apply(this, arguments);
    },

    toggleSortState: function() {
        var me = this,
            idx,
            nextIdx;

        if (me.sortable) {
            idx = Ext.Array.indexOf(me.possibleSortStates, me.sortState);

            nextIdx = (idx + 1) % me.possibleSortStates.length;
            me.setSortState(me.possibleSortStates[nextIdx]);
        }
    },

    doSort: function(state) {
        var tablePanel = this.up('tablepanel'),
            store = tablePanel.store;

        // If the owning Panel's store is a NodeStore, this means that we are the unlocked side
        // of a locked TreeGrid. We must use the TreeStore's sort method because we cannot
        // reorder the NodeStore - that would break the tree.
        if (tablePanel.ownerLockable && store.isNodeStore) {
            store = tablePanel.ownerLockable.lockedGrid.store;
        }
        store.sort({
            property: this.getSortParam(),
            direction: state
        });
    },

    /**
     * Returns the parameter to sort upon when sorting this header. By default this returns the dataIndex and will not
     * need to be overriden in most cases.
     * @return {String}
     */
    getSortParam: function() {
        return this.dataIndex;
    },

    setSortState: function(state, skipClear, initial) {
        var me = this,
            ascCls = me.ascSortCls,
            descCls = me.descSortCls,
            ownerHeaderCt = me.getOwnerHeaderCt(),
            oldSortState = me.sortState;

         state = state || null;

        if (!me.sorting && oldSortState !== state && (me.getSortParam() != null)) {
            // don't trigger a sort on the first time, we just want to update the UI
            if (state && !initial) {
                // when sorting, it will call setSortState on the header again once
                // refresh is called
                me.sorting = true;
                me.doSort(state);
                me.sorting = false;
            }
            switch (state) {
                case 'DESC':
                    me.addCls(descCls);
                    me.removeCls(ascCls);
                    break;
                case 'ASC':
                    me.addCls(ascCls);
                    me.removeCls(descCls);
                    break;
                default:
                    me.removeCls([ascCls, descCls]);
            }
            if (ownerHeaderCt && !me.triStateSort && !skipClear) {
                ownerHeaderCt.clearOtherSortStates(me);
            }
            me.sortState = state;
            // we only want to fire the event if we have a null state when using triStateSort
            if (me.triStateSort || state != null) {
                ownerHeaderCt.fireEvent('sortchange', ownerHeaderCt, me, state);
            }
        }
    },

    /**
     * Determines whether the UI should be allowed to offer an option to hide this column.
     *
     * A column may *not* be hidden if to do so would leave the grid with no visible columns.
     *
     * This is used to determine the enabled/disabled state of header hide menu items.
     */
    isHideable: function() {
        var result = {
                hideCandidate: this,
                result: this.hideable
            };

        if (result.result) {
            this.ownerCt.bubble(this.hasOtherMenuEnabledChildren, null, [result]);
        }
        return result.result;
    },

    // Private bubble function used in determining whether this column is hideable.
    // Executes in the scope of each component in the bubble sequence
    hasOtherMenuEnabledChildren: function(result) {
        var visibleChildren,
            count;

        // If we've bubbled out the top of the topmost HeaderContainer without finding a level with at least one visible,
        // menu-enabled child *which is not the hideCandidate*, no hide!
        if (!this.isXType('headercontainer')) {
            result.result = false;
            return false;
        }
        // If we find an ancestor level with at leat one visible, menu-enabled child *which is not the hideCandidate*,
        // then the hideCandidate is hideable.
        // Note that we are not using CQ #id matchers - ':not(#' + result.hideCandidate.id + ')' - to exclude
        // the hideCandidate because CQ queries are cached for the document's lifetime.
        visibleChildren = this.query('>:not([hidden]):not([menuDisabled])');
        count = visibleChildren.length;
        if (Ext.Array.contains(visibleChildren, result.hideCandidate)) {
            count--;
        }
        if (count) {
            return false;
        }
        // If we go up, it's because the hideCandidate was the only hideable child, so *this* becomes the hide candidate.
        result.hideCandidate = this;
    },

    /**
     * Determines whether the UI should be allowed to offer an option to lock or unlock this column. Note
     * that this includes dragging a column into the opposite side of a {@link Ext.panel.Table#enableLocking lockable} grid.
     *
     * A column may *not* be moved from one side to the other of a {@link Ext.panel.Table#enableLocking lockable} grid
     * if to do so would leave one side with no visible columns.
     *
     * This is used to determine the enabled/disabled state of the lock/unlock
     * menu item used in {@link Ext.panel.Table#enableLocking lockable} grids, and to determine dropppabilty when dragging a header.
     */
    isLockable: function() {
        var result = {
                result: this.lockable !== false
            };

        if (result.result) {
            this.ownerCt.bubble(this.hasMultipleVisibleChildren, null, [result]);
        }
        return result.result;
    },

    /*
     * Determines whether this column is in the locked side of a grid. It may be a descendant node of a locked column
     * and as such will *not* have the {@link #locked} flag set.
     */
    isLocked: function() {
        return this.locked || !!this.up('[isColumn][locked]', '[isRootHeader]');
    },

    // Private bubble function used in determining whether this column is lockable.
    // Executes in the scope of each component in the bubble sequence
    hasMultipleVisibleChildren: function(result) {
        // If we've bubbled out the top of the topmost HeaderContainer without finding a level with more than one visible child, no hide!
        if (!this.isXType('headercontainer')) {
            result.result = false;
            return false;
        }
        // If we find an ancestor level with more than one visible child, it's fine to hide
        if (this.query('>:not([hidden])').length > 1) {
            return false;
        }
    },

    hide: function(fromOwner) {
        var me = this,
            ownerHeaderCt = me.getOwnerHeaderCt(),
            owner = me.ownerCt,
            ownerIsGroup,
            item, items, len, i;
            
        // If we have no ownerHeaderCt, it's during object construction, so
        // just set the hidden flag and jump out
        if (!ownerHeaderCt) {
            me.callParent();
            return me;
        }
            
        if (me.rendered && !me.isVisible()) {
            // Already hidden
            return me;
        }

        // Save our last shown width so we can gain space when shown back into fully flexed HeaderContainer.
        // If we are, say, flex: 1 and all others are fixed width, then removing will do a layout which will
        // convert all widths to flexes which will mean this flex value is too small.
        if (ownerHeaderCt.forceFit) {
            me.visibleSiblingCount = ownerHeaderCt.getVisibleGridColumns().length - 1;
            if (me.flex) {
                me.savedWidth = me.getWidth();
                me.flex = null;
            }
        }

        ownerIsGroup = owner.isGroupHeader;

        // owner is a group, hide call didn't come from the owner
        if (ownerIsGroup && !fromOwner) {
            items = owner.query('>:not([hidden])');
            // The owner only has one item that isn't hidden and it's me; hide the owner.
            if (items.length === 1 && items[0] == me) {
                me.ownerCt.hide();
                return;
            }
        }

        Ext.suspendLayouts();

        if (me.isGroupHeader) {
            items = me.items.items;
            for (i = 0, len = items.length; i < len; i++) {
                item = items[i];
                if (!item.hidden) {
                    item.hide(true);
                }
            }
        }

        me.callParent();
        // Notify owning HeaderContainer
        ownerHeaderCt.onHeaderHide(me);

        Ext.resumeLayouts(true);
        return me;
    },

    show: function(fromOwner, fromChild) {
        var me = this,
            ownerHeaderCt = me.getOwnerHeaderCt(),
            ownerCt = me.ownerCt,
            items,
            len, i,
            item;
            
        if (me.isVisible()) {
            return me;
        }

        if (me.rendered) {
            // Size all other columns to accommodate re-shown column.
            if (ownerHeaderCt.forceFit) {
                ownerHeaderCt.applyForceFit(me);
            }
        }

        Ext.suspendLayouts();

        // If a sub header, ensure that the group header is visible
        if (me.isSubHeader && ownerCt.hidden) {
            ownerCt.show(false, true);
        }

        me.callParent(arguments);

        // If we've just shown a group with all its sub headers hidden, then show all its sub headers
        if (me.isGroupHeader && fromChild !== true && !me.query(':not([hidden])').length) {
            items = me.items.items;
            for (i = 0, len = items.length; i < len; i++) {
                item = items[i];
                if (item.hidden) {
                    item.show(true);
                }
            }
        }

        Ext.resumeLayouts(true);

        // Notify owning HeaderContainer AFTER layout has been flushed so that header and headerCt widths are all correct
        ownerCt = me.getOwnerHeaderCt();
        if (ownerCt) {
            ownerCt.onHeaderShow(me);
        }
    },

    getDesiredWidth: function() {
        var me = this;
        if (me.rendered && me.componentLayout && me.componentLayout.lastComponentSize) {
            // headers always have either a width or a flex
            // because HeaderContainer sets a defaults width
            // therefore we can ignore the natural width
            // we use the componentLayout's tracked width so that
            // we can calculate the desired width when rendered
            // but not visible because its being obscured by a layout
            return me.componentLayout.lastComponentSize.width;
        // Flexed but yet to be rendered this could be the case
        // where a HeaderContainer and Headers are simply used as data
        // structures and not rendered.
        }
        else if (me.flex) {
            // this is going to be wrong, the defaultWidth
            return me.width;
        }
        else {
            return me.width;
        }
    },
    
    getCellId: function() {
        return Ext.baseCSSPrefix + 'grid-cell-headerId-' + this.getItemId();
    },

    getCellSelector: function() {
        return '.' + this.getCellId();
    },

    getCellInnerSelector: function() {
        return this.getCellSelector() + ' .' + Ext.baseCSSPrefix + 'grid-cell-inner';
    },

    isOnLeftEdge: function(e) {
        return (e.getXY()[0] - this.getX() <= this.handleWidth);
    },

    isOnRightEdge: function(e) {
        return (this.getX() + this.getWidth() - e.getXY()[0] <= this.handleWidth);
    },

    // Called when the column menu is activated/deactivated.
    // Change the UI to indicate active/inactive menu
    setMenuActive: function(isMenuOpen) {
        this.titleEl[isMenuOpen ? 'addCls' : 'removeCls'](this.headerOpenCls);
    }

    // intentionally omit getEditor and setEditor definitions bc we applyIf into columns
    // when the editing plugin is injected

    /**
     * @method getEditor
     * Retrieves the editing field for editing associated with this header. Returns false if there is no field
     * associated with the Header the method will return false. If the field has not been instantiated it will be
     * created. Note: These methods only have an implementation if an Editing plugin has been enabled on the grid.
     * @param {Object} record The {@link Ext.data.Model Model} instance being edited.
     * @param {Object} defaultField An object representing a default field to be created
     * @return {Ext.form.field.Field} field
     */
    /**
     * @method setEditor
     * Sets the form field to be used for editing. Note: This method only has an implementation if an Editing plugin has
     * been enabled on the grid.
     * @param {Object} field An object representing a field to be created. If no xtype is specified a 'textfield' is
     * assumed.
     */
});

/**
 * A Grid header type which renders an icon, or a series of icons in a grid cell, and offers a scoped click
 * handler for each icon.
 *
 *     @example
 *     Ext.create('Ext.data.Store', {
 *         storeId:'employeeStore',
 *         fields:['firstname', 'lastname', 'seniority', 'dep', 'hired'],
 *         data:[
 *             {firstname:"Michael", lastname:"Scott"},
 *             {firstname:"Dwight", lastname:"Schrute"},
 *             {firstname:"Jim", lastname:"Halpert"},
 *             {firstname:"Kevin", lastname:"Malone"},
 *             {firstname:"Angela", lastname:"Martin"}
 *         ]
 *     });
 *
 *     Ext.create('Ext.grid.Panel', {
 *         title: 'Action Column Demo',
 *         store: Ext.data.StoreManager.lookup('employeeStore'),
 *         columns: [
 *             {text: 'First Name',  dataIndex:'firstname'},
 *             {text: 'Last Name',  dataIndex:'lastname'},
 *             {
 *                 xtype:'actioncolumn',
 *                 width:50,
 *                 items: [{
 *                     icon: 'extjs/examples/shared/icons/fam/cog_edit.png',  // Use a URL in the icon config
 *                     tooltip: 'Edit',
 *                     handler: function(grid, rowIndex, colIndex) {
 *                         var rec = grid.getStore().getAt(rowIndex);
 *                         alert("Edit " + rec.get('firstname'));
 *                     }
 *                 },{
 *                     icon: 'extjs/examples/restful/images/delete.png',
 *                     tooltip: 'Delete',
 *                     handler: function(grid, rowIndex, colIndex) {
 *                         var rec = grid.getStore().getAt(rowIndex);
 *                         alert("Terminate " + rec.get('firstname'));
 *                     }
 *                 }]
 *             }
 *         ],
 *         width: 250,
 *         renderTo: Ext.getBody()
 *     });
 *
 * The action column can be at any index in the columns array, and a grid can have any number of
 * action columns.
 */
Ext.define('Ext.grid.column.Action', {
    extend:  Ext.grid.column.Column ,
    alias: ['widget.actioncolumn'],
    alternateClassName: 'Ext.grid.ActionColumn',

    /**
     * @cfg {String} icon
     * The URL of an image to display as the clickable element in the column.
     *
     * Defaults to `{@link Ext#BLANK_IMAGE_URL}`.
     */
    /**
     * @cfg {String} iconCls
     * A CSS class to apply to the icon image. To determine the class dynamically, configure the Column with
     * a `{@link #getClass}` function.
     */
    /**
     * @cfg {Function} handler
     * A function called when the icon is clicked.
     * @cfg {Ext.view.Table} handler.view The owning TableView.
     * @cfg {Number} handler.rowIndex The row index clicked on.
     * @cfg {Number} handler.colIndex The column index clicked on.
     * @cfg {Object} handler.item The clicked item (or this Column if multiple {@link #cfg-items} were not configured).
     * @cfg {Event} handler.e The click event.
     * @cfg {Ext.data.Model} handler.record The Record underlying the clicked row.
     * @cfg {HTMLElement} handler.row The table row clicked upon.
     */
    /**
     * @cfg {Object} scope
     * The scope (`this` reference) in which the `{@link #handler}`, `{@link #getClass}`, `{@link #cfg-isDisabled}` and `{@link #getTip}` fuctions are executed.
     * Defaults to this Column.
     */
    /**
     * @cfg {String} tooltip
     * A tooltip message to be displayed on hover. {@link Ext.tip.QuickTipManager#init Ext.tip.QuickTipManager} must
     * have been initialized.
     * 
     * The tooltip may also be determined on a row by row basis by configuring a {@link #getTip} method.
     */
    /**
     * @cfg {Boolean} disabled
     * If true, the action will not respond to click events, and will be displayed semi-opaque.
     * 
     * This Column may also be disabled on a row by row basis by configuring a {@link #cfg-isDisabled} method.
     */
    /**
     * @cfg {Boolean} [stopSelection=true]
     * Prevent grid selection upon mousedown.
     */
    /**
     * @cfg {Function} getClass
     * A function which returns the CSS class to apply to the icon image.
     * @cfg {Object} getClass.v The value of the column's configured field (if any).
     * @cfg {Object} getClass.metadata An object in which you may set the following attributes:
     * @cfg {String} getClass.metadata.css A CSS class name to add to the cell's TD element.
     * @cfg {String} getClass.metadata.attr An HTML attribute definition string to apply to the data container
     * element *within* the table cell (e.g. 'style="color:red;"').
     * @cfg {Ext.data.Model} getClass.r The Record providing the data.
     * @cfg {Number} getClass.rowIndex The row index.
     * @cfg {Number} getClass.colIndex The column index.
     * @cfg {Ext.data.Store} getClass.store The Store which is providing the data Model.
     */
    /**
     * @cfg {Function} isDisabled A function which determines whether the action item for any row is disabled and returns `true` or `false`.
     * @cfg {Ext.view.Table} isDisabled.view The owning TableView.
     * @cfg {Number} isDisabled.rowIndex The row index.
     * @cfg {Number} isDisabled.colIndex The column index.
     * @cfg {Object} isDisabled.item The clicked item (or this Column if multiple {@link #cfg-items} were not configured).
     * @cfg {Ext.data.Model} isDisabled.record The Record underlying the row.
     */
    /**
     * @cfg {Function} getTip A function which returns the tooltip string for any row.
     * @cfg {Object} getTip.v The value of the column's configured field (if any).
     * @cfg {Object} getTip.metadata An object in which you may set the following attributes:
     * @cfg {String} getTip.metadata.css A CSS class name to add to the cell's TD element.
     * @cfg {String} getTip.metadata.attr An HTML attribute definition string to apply to the data
     * container element _within_ the table cell (e.g. 'style="color:red;"').
     * @cfg {Ext.data.Model} getTip.r The Record providing the data.
     * @cfg {Number} getTip.rowIndex The row index.
     * @cfg {Number} getTip.colIndex The column index.
     * @cfg {Ext.data.Store} getTip.store The Store which is providing the data Model.
     *
     */
    /**
     * @cfg {Object[]} items
     * An Array which may contain multiple icon definitions, each element of which may contain:
     *
     * @cfg {String} items.icon The url of an image to display as the clickable element in the column.
     *
     * @cfg {String} items.iconCls A CSS class to apply to the icon image. To determine the class dynamically,
     * configure the item with a `getClass` function.
     *
     * @cfg {Function} items.getClass A function which returns the CSS class to apply to the icon image.
     * @cfg {Object} items.getClass.v The value of the column's configured field (if any).
     * @cfg {Object} items.getClass.metadata An object in which you may set the following attributes:
     * @cfg {String} items.getClass.metadata.css A CSS class name to add to the cell's TD element.
     * @cfg {String} items.getClass.metadata.attr An HTML attribute definition string to apply to the data
     * container element _within_ the table cell (e.g. 'style="color:red;"').
     * @cfg {Ext.data.Model} items.getClass.r The Record providing the data.
     * @cfg {Number} items.getClass.rowIndex The row index.
     * @cfg {Number} items.getClass.colIndex The column index.
     * @cfg {Ext.data.Store} items.getClass.store The Store which is providing the data Model.
     *
     * @cfg {Function} items.handler A function called when the icon is clicked.
     * @cfg {Ext.view.Table} items.handler.view The owning TableView.
     * @cfg {Number} items.handler.rowIndex The row index clicked on.
     * @cfg {Number} items.handler.colIndex The column index clicked on.
     * @cfg {Object} items.handler.item The clicked item (or this Column if multiple {@link #cfg-items} were not configured).
     * @cfg {Event} items.handler.e The click event.
     * @cfg {Ext.data.Model} items.handler.record The Record underlying the clicked row.
     * @cfg {HTMLElement} items.row The table row clicked upon.
     *
     * @cfg {Function} items.isDisabled A function which determines whether the action item for any row is disabled and returns `true` or `false`.
     * @cfg {Ext.view.Table} items.isDisabled.view The owning TableView.
     * @cfg {Number} items.isDisabled.rowIndex The row index.
     * @cfg {Number} items.isDisabled.colIndex The column index.
     * @cfg {Object} items.isDisabled.item The clicked item (or this Column if multiple {@link #cfg-items} were not configured).
     * @cfg {Ext.data.Model} items.isDisabled.record The Record underlying the row.
     *
     * @cfg {Function} items.getTip A function which returns the tooltip string for any row.
     * @cfg {Object} items.getTip.v The value of the column's configured field (if any).
     * @cfg {Object} items.getTip.metadata An object in which you may set the following attributes:
     * @cfg {String} items.getTip.metadata.css A CSS class name to add to the cell's TD element.
     * @cfg {String} items.getTip.metadata.attr An HTML attribute definition string to apply to the data
     * container element _within_ the table cell (e.g. 'style="color:red;"').
     * @cfg {Ext.data.Model} items.getTip.r The Record providing the data.
     * @cfg {Number} items.getTip.rowIndex The row index.
     * @cfg {Number} items.getTip.colIndex The column index.
     * @cfg {Ext.data.Store} items.getTip.store The Store which is providing the data Model.
     *
     * @cfg {Object} items.scope The scope (`this` reference) in which the `handler`, `getClass`, `isDisabled` and `getTip` functions
     * are executed. Fallback defaults are this Column's configured scope, then this Column.
     *
     * @cfg {String} items.tooltip A tooltip message to be displayed on hover.
     * {@link Ext.tip.QuickTipManager#init Ext.tip.QuickTipManager} must have been initialized.
     * 
     * The tooltip may also be determined on a row by row basis by configuring a `getTip` method.
     *
     * @cfg {Boolean} items.disabled If true, the action will not respond to click events, and will be displayed semi-opaque.
     * 
     * This item may also be disabled on a row by row basis by configuring an `isDisabled` method.
     */
    /**
     * @property {Array} items
     * An array of action items copied from the configured {@link #cfg-items items} configuration. Each will have
     * an `enable` and `disable` method added which will enable and disable the associated action, and
     * update the displayed icon accordingly.
     */

    actionIdRe: new RegExp(Ext.baseCSSPrefix + 'action-col-(\\d+)'),

    /**
     * @cfg {String} altText
     * The alt text to use for the image element.
     */
    altText: '',

    /**
     * @cfg {String} [menuText=<i>Actions</i>]
     * Text to display in this column's menu item if no {@link #text} was specified as a header.
     */
    menuText: '<i>Actions</i>',

    sortable: false,

    innerCls: Ext.baseCSSPrefix + 'grid-cell-inner-action-col',

    constructor: function(config) {
        var me = this,
            cfg = Ext.apply({}, config),
            // Items may be defined on the prototype
            items = cfg.items || me.items || [me],
            hasGetClass,
            i,
            len;


        me.origRenderer = cfg.renderer || me.renderer;
        me.origScope = cfg.scope || me.scope;
        
        me.renderer = me.scope = cfg.renderer = cfg.scope = null;
        
        // This is a Container. Delete the items config to be reinstated after construction.
        cfg.items = null;
        me.callParent([cfg]);

        // Items is an array property of ActionColumns
        me.items = items;
        
        for (i = 0, len = items.length; i < len; ++i) {
            if (items[i].getClass) {
                hasGetClass = true;
                break;
            }
        }
        
        // Also need to check for getClass, since it changes how the cell renders
        if (me.origRenderer || hasGetClass) {
            me.hasCustomRenderer = true;
        }
    },
    
    // Renderer closure iterates through items creating an <img> element for each and tagging with an identifying
    // class name x-action-col-{n}
    defaultRenderer: function(v, meta, record, rowIdx, colIdx, store, view){
        var me = this,
            prefix = Ext.baseCSSPrefix,
            scope = me.origScope || me,
            items = me.items,
            len = items.length,
            i = 0,
            item, ret, disabled, tooltip;
 
        // Allow a configured renderer to create initial value (And set the other values in the "metadata" argument!)
        // Assign a new variable here, since if we modify "v" it will also modify the arguments collection, meaning
        // we will pass an incorrect value to getClass/getTip
        ret = Ext.isFunction(me.origRenderer) ? me.origRenderer.apply(scope, arguments) || '' : '';

        meta.tdCls += ' ' + Ext.baseCSSPrefix + 'action-col-cell';
        for (; i < len; i++) {
            item = items[i];

            disabled = item.disabled || (item.isDisabled ? item.isDisabled.call(item.scope || scope, view, rowIdx, colIdx, item, record) : false);
            tooltip = disabled ? null : (item.tooltip || (item.getTip ? item.getTip.apply(item.scope || scope, arguments) : null));

            // Only process the item action setup once.
            if (!item.hasActionConfiguration) {

                // Apply our documented default to all items
                item.stopSelection = me.stopSelection;
                item.disable = Ext.Function.bind(me.disableAction, me, [i], 0);
                item.enable = Ext.Function.bind(me.enableAction, me, [i], 0);
                item.hasActionConfiguration = true;
            }

            ret += '<img role="button" alt="' + (item.altText || me.altText) + '" src="' + (item.icon || Ext.BLANK_IMAGE_URL) +
                '" class="' + prefix + 'action-col-icon ' + prefix + 'action-col-' + String(i) + ' ' + (disabled ? prefix + 'item-disabled' : ' ') +
                ' ' + (Ext.isFunction(item.getClass) ? item.getClass.apply(item.scope || scope, arguments) : (item.iconCls || me.iconCls || '')) + '"' +
                (tooltip ? ' data-qtip="' + tooltip + '"' : '') + ' />';
        }
        return ret;    
    },

    /**
     * Enables this ActionColumn's action at the specified index.
     * @param {Number/Ext.grid.column.Action} index
     * @param {Boolean} [silent=false]
     */
    enableAction: function(index, silent) {
        var me = this;

        if (!index) {
            index = 0;
        } else if (!Ext.isNumber(index)) {
            index = Ext.Array.indexOf(me.items, index);
        }
        me.items[index].disabled = false;
        me.up('tablepanel').el.select('.' + Ext.baseCSSPrefix + 'action-col-' + index).removeCls(me.disabledCls);
        if (!silent) {
            me.fireEvent('enable', me);
        }
    },

    /**
     * Disables this ActionColumn's action at the specified index.
     * @param {Number/Ext.grid.column.Action} index
     * @param {Boolean} [silent=false]
     */
    disableAction: function(index, silent) {
        var me = this;

        if (!index) {
            index = 0;
        } else if (!Ext.isNumber(index)) {
            index = Ext.Array.indexOf(me.items, index);
        }
        me.items[index].disabled = true;
        me.up('tablepanel').el.select('.' + Ext.baseCSSPrefix + 'action-col-' + index).addCls(me.disabledCls);
        if (!silent) {
            me.fireEvent('disable', me);
        }
    },

    destroy: function() {
        delete this.items;
        delete this.renderer;
        return this.callParent(arguments);
    },

    /**
     * @private
     * Process and refire events routed from the GridView's processEvent method.
     * Also fires any configured click handlers. By default, cancels the mousedown event to prevent selection.
     * Returns the event handler's status to allow canceling of GridView's bubbling process.
     */
    processEvent : function(type, view, cell, recordIndex, cellIndex, e, record, row){
        var me = this,
            target = e.getTarget(),
            match,
            item, fn,
            key = type == 'keydown' && e.getKey(),
            disabled;

        // If the target was not within a cell (ie it's a keydown event from the View), then
        // rely on the selection data injected by View.processUIEvent to grab the
        // first action icon from the selected cell.
        if (key && !Ext.fly(target).findParent(view.getCellSelector())) {
            target = Ext.fly(cell).down('.' + Ext.baseCSSPrefix + 'action-col-icon', true);
        }

        // NOTE: The statement below tests the truthiness of an assignment.
        if (target && (match = target.className.match(me.actionIdRe))) {
            item = me.items[parseInt(match[1], 10)];
            disabled = item.disabled || (item.isDisabled ? item.isDisabled.call(item.scope || me.origScope || me, view, recordIndex, cellIndex, item, record) : false);
            if (item && !disabled) {
                if (type == 'click' || (key == e.ENTER || key == e.SPACE)) {
                    fn = item.handler || me.handler;
                    if (fn) {
                        fn.call(item.scope || me.origScope || me, view, recordIndex, cellIndex, item, e, record, row);
                    }
                } else if (type == 'mousedown' && item.stopSelection !== false) {
                    return false;
                }
            }
        }
        return me.callParent(arguments);
    },

    cascade: function(fn, scope) {
        fn.call(scope||this, this);
    },

    // Private override because this cannot function as a Container, and it has an items property which is an Array, NOT a MixedCollection.
    getRefItems: function() {
        return [];
    }
});

/**
 * A Column definition class which renders boolean data fields.  See the {@link Ext.grid.column.Column#xtype xtype}
 * config option of {@link Ext.grid.column.Column} for more details.
 *
 *     @example
 *     Ext.create('Ext.data.Store', {
 *        storeId:'sampleStore',
 *        fields:[
 *            {name: 'framework', type: 'string'},
 *            {name: 'rocks', type: 'boolean'}
 *        ],
 *        data:{'items':[
 *            { 'framework': "Ext JS 4",     'rocks': true  },
 *            { 'framework': "Sencha Touch", 'rocks': true  },
 *            { 'framework': "Ext GWT",      'rocks': true  }, 
 *            { 'framework': "Other Guys",   'rocks': false } 
 *        ]},
 *        proxy: {
 *            type: 'memory',
 *            reader: {
 *                type: 'json',
 *                root: 'items'
 *            }
 *        }
 *     });
 *     
 *     Ext.create('Ext.grid.Panel', {
 *         title: 'Boolean Column Demo',
 *         store: Ext.data.StoreManager.lookup('sampleStore'),
 *         columns: [
 *             { text: 'Framework',  dataIndex: 'framework', flex: 1 },
 *             {
 *                 xtype: 'booleancolumn', 
 *                 text: 'Rocks',
 *                 trueText: 'Yes',
 *                 falseText: 'No', 
 *                 dataIndex: 'rocks'
 *             }
 *         ],
 *         height: 200,
 *         width: 400,
 *         renderTo: Ext.getBody()
 *     });
 */
Ext.define('Ext.grid.column.Boolean', {
    extend:  Ext.grid.column.Column ,
    alias: ['widget.booleancolumn'],
    alternateClassName: 'Ext.grid.BooleanColumn',

    //<locale>
    /**
     * @cfg {String} trueText
     * The string returned by the renderer when the column value is not falsey.
     */
    trueText: 'true',
    //</locale>

    //<locale>
    /**
     * @cfg {String} falseText
     * The string returned by the renderer when the column value is falsey (but not undefined).
     */
    falseText: 'false',
    //</locale>

    /**
     * @cfg {String} undefinedText
     * The string returned by the renderer when the column value is undefined.
     */
    undefinedText: '&#160;',

    /**
     * @cfg {Object} renderer
     * @hide
     */
    
    /**
     * @cfg {Object} scope
     * @hide
     */

    defaultRenderer: function(value){
        if (value === undefined) {
            return this.undefinedText;
        }
        
        if (!value || value === 'false') {
            return this.falseText;
        }
        return this.trueText;
    }
});

/**
 * A Column subclass which renders a checkbox in each column cell which toggles the truthiness of the associated data field on click.
 *
 * Example usage:
 *
 *     @example
 *     var store = Ext.create('Ext.data.Store', {
 *         fields : ['name', 'email', 'phone', 'active'],
 *         data   : {
 *             items : [
 *                 { name : 'Lisa',  email : 'lisa@simpsons.com',  phone : '555-111-1224', active : true  },
 *                 { name : 'Bart',  email : 'bart@simpsons.com',  phone : '555-222-1234', active : true  },
 *                 { name : 'Homer', email : 'home@simpsons.com',  phone : '555-222-1244', active : false },
 *                 { name : 'Marge', email : 'marge@simpsons.com', phone : '555-222-1254', active : true  }
 *             ]
 *         },
 *         proxy  : {
 *             type   : 'memory',
 *             reader : {
 *                 type : 'json',
 *                 root : 'items'
 *             }
 *         }
 *     });
 *
 *     Ext.create('Ext.grid.Panel', {
 *         title    : 'Simpsons',
 *         height   : 200,
 *         width    : 400,
 *         renderTo : Ext.getBody(),
 *         store    : store,
 *         columns  : [
 *             { text : 'Name', dataIndex : 'name' },
 *             { text : 'Email', dataIndex : 'email', flex : 1 },
 *             { text : 'Phone', dataIndex : 'phone' },
 *             { xtype : 'checkcolumn', text : 'Active', dataIndex : 'active' }
 *         ]
 *     });
 *
 * The check column can be at any index in the columns array.
 */
Ext.define('Ext.grid.column.Check', {
    extend:  Ext.grid.column.Column ,
    alternateClassName: ['Ext.ux.CheckColumn', 'Ext.grid.column.CheckColumn'],
    alias: 'widget.checkcolumn',

    /**
     * @cfg
     * @hide
     * Overridden from base class. Must center to line up with editor.
     */
    align: 'center',

    /**
     * @cfg {Boolean} [stopSelection=true]
     * Prevent grid selection upon mousedown.
     */
    stopSelection: true,

    tdCls: Ext.baseCSSPrefix + 'grid-cell-checkcolumn',
    innerCls: Ext.baseCSSPrefix + 'grid-cell-inner-checkcolumn',

    clickTargetName: 'el',

    constructor: function() {
        this.addEvents(
            /**
             * @event beforecheckchange
             * Fires when before checked state of a row changes.
             * The change may be vetoed by returning `false` from a listener.
             * @param {Ext.ux.CheckColumn} this CheckColumn
             * @param {Number} rowIndex The row index
             * @param {Boolean} checked True if the box is to be checked
             */
            'beforecheckchange',
            /**
             * @event checkchange
             * Fires when the checked state of a row changes
             * @param {Ext.ux.CheckColumn} this CheckColumn
             * @param {Number} rowIndex The row index
             * @param {Boolean} checked True if the box is now checked
             */
            'checkchange'
        );
        this.scope = this;
        this.callParent(arguments);
    },

    /**
     * @private
     * Process and refire events routed from the GridView's processEvent method.
     */
    processEvent: function(type, view, cell, recordIndex, cellIndex, e, record, row) {
        var me = this,
            key = type === 'keydown' && e.getKey(),
            mousedown = type == 'mousedown';

        if (!me.disabled && (mousedown || (key == e.ENTER || key == e.SPACE))) {
            var dataIndex = me.dataIndex,
                checked = !record.get(dataIndex);

            // Allow apps to hook beforecheckchange
            if (me.fireEvent('beforecheckchange', me, recordIndex, checked) !== false) {
                record.set(dataIndex, checked);
                me.fireEvent('checkchange', me, recordIndex, checked);

                // Mousedown on the now nonexistent cell causes the view to blur, so stop it continuing.
                if (mousedown) {
                    e.stopEvent();
                }

                // Selection will not proceed after this because of the DOM update caused by the record modification
                // Invoke the SelectionModel unless configured not to do so
                if (!me.stopSelection) {
                    view.selModel.selectByPosition({
                        row: recordIndex,
                        column: cellIndex
                    });
                }

                // Prevent the view from propagating the event to the selection model - we have done that job.
                return false;
            } else {
                // Prevent the view from propagating the event to the selection model if configured to do so.
                return !me.stopSelection;
            }
        } else {
            return me.callParent(arguments);
        }
    },

    /**
     * Enables this CheckColumn.
     */
    onEnable: function() {
        this.callParent(arguments);
        this._setDisabled(false);
    },

    /**
     * Disables this CheckColumn.
     */
    onDisable: function() {
        this._setDisabled(true);
    },
    
    // Don't want to conflict with the Component method
    _setDisabled: function(disabled) {
        var me = this,
            cls = me.disabledCls,
            items;

        items = me.up('tablepanel').el.select(me.getCellSelector());
        if (disabled) {
            items.addCls(cls);
        } else {
            items.removeCls(cls);
        }
    },

    // Note: class names are not placed on the prototype bc renderer scope
    // is not in the header.
    renderer : function(value, meta) {
        var cssPrefix = Ext.baseCSSPrefix,
            cls = cssPrefix + 'grid-checkcolumn';

        if (this.disabled) {
            meta.tdCls += ' ' + this.disabledCls;
        }
        if (value) {
            cls += ' ' + cssPrefix + 'grid-checkcolumn-checked';
        }
        return '<img class="' + cls + '" src="' + Ext.BLANK_IMAGE_URL + '"/>';
    }
});

/**
 * A Column definition class which renders a passed date according to the default locale, or a configured
 * {@link #format}.
 *
 *     @example
 *     Ext.create('Ext.data.Store', {
 *         storeId:'sampleStore',
 *         fields:[
 *             { name: 'symbol', type: 'string' },
 *             { name: 'date',   type: 'date' },
 *             { name: 'change', type: 'number' },
 *             { name: 'volume', type: 'number' },
 *             { name: 'topday', type: 'date' }                        
 *         ],
 *         data:[
 *             { symbol: "msft",   date: '2011/04/22', change: 2.43, volume: 61606325, topday: '04/01/2010' },
 *             { symbol: "goog",   date: '2011/04/22', change: 0.81, volume: 3053782,  topday: '04/11/2010' },
 *             { symbol: "apple",  date: '2011/04/22', change: 1.35, volume: 24484858, topday: '04/28/2010' },            
 *             { symbol: "sencha", date: '2011/04/22', change: 8.85, volume: 5556351,  topday: '04/22/2010' }            
 *         ]
 *     });
 *     
 *     Ext.create('Ext.grid.Panel', {
 *         title: 'Date Column Demo',
 *         store: Ext.data.StoreManager.lookup('sampleStore'),
 *         columns: [
 *             { text: 'Symbol',   dataIndex: 'symbol', flex: 1 },
 *             { text: 'Date',     dataIndex: 'date',   xtype: 'datecolumn',   format:'Y-m-d' },
 *             { text: 'Change',   dataIndex: 'change', xtype: 'numbercolumn', format:'0.00' },
 *             { text: 'Volume',   dataIndex: 'volume', xtype: 'numbercolumn', format:'0,000' },
 *             { text: 'Top Day',  dataIndex: 'topday', xtype: 'datecolumn',   format:'l' }            
 *         ],
 *         height: 200,
 *         width: 450,
 *         renderTo: Ext.getBody()
 *     });
 */
Ext.define('Ext.grid.column.Date', {
    extend:  Ext.grid.column.Column ,
    alias: ['widget.datecolumn'],
                           
    alternateClassName: 'Ext.grid.DateColumn',

    /**
     * @cfg {String} format
     * A formatting string as used by {@link Ext.Date#format} to format a Date for this Column.
     *
     * Defaults to the default date from {@link Ext.Date#defaultFormat} which itself my be overridden
     * in a locale file.
     */
    
    /**
     * @cfg {Object} renderer
     * @hide
     */
    
    /**
     * @cfg {Object} scope
     * @hide
     */

    initComponent: function(){
        if (!this.format) {
            this.format = Ext.Date.defaultFormat;
        }
        
        this.callParent(arguments);
    },
    
    defaultRenderer: function(value){
        return Ext.util.Format.date(value, this.format);
    }
});

/**
 * A Column definition class which renders a numeric data field according to a {@link #format} string.
 *
 *     @example
 *     Ext.create('Ext.data.Store', {
 *        storeId:'sampleStore',
 *        fields:[
 *            { name: 'symbol', type: 'string' },
 *            { name: 'price',  type: 'number' },
 *            { name: 'change', type: 'number' },
 *            { name: 'volume', type: 'number' }
 *        ],
 *        data:[
 *            { symbol: "msft",   price: 25.76,  change: 2.43, volume: 61606325 },
 *            { symbol: "goog",   price: 525.73, change: 0.81, volume: 3053782  },
 *            { symbol: "apple",  price: 342.41, change: 1.35, volume: 24484858 },
 *            { symbol: "sencha", price: 142.08, change: 8.85, volume: 5556351  }
 *        ]
 *     });
 *     
 *     Ext.create('Ext.grid.Panel', {
 *         title: 'Number Column Demo',
 *         store: Ext.data.StoreManager.lookup('sampleStore'),
 *         columns: [
 *             { text: 'Symbol',         dataIndex: 'symbol', flex: 1 },
 *             { text: 'Current Price',  dataIndex: 'price',  renderer: Ext.util.Format.usMoney },
 *             { text: 'Change',         dataIndex: 'change', xtype: 'numbercolumn', format:'0.00' },
 *             { text: 'Volume',         dataIndex: 'volume', xtype: 'numbercolumn', format:'0,000' }
 *         ],
 *         height: 200,
 *         width: 400,
 *         renderTo: Ext.getBody()
 *     });
 */
Ext.define('Ext.grid.column.Number', {
    extend:  Ext.grid.column.Column ,
    alias: ['widget.numbercolumn'],
                                  
    alternateClassName: 'Ext.grid.NumberColumn',

    //<locale>
    /**
     * @cfg {String} format
     * A formatting string as used by {@link Ext.util.Format#number} to format a numeric value for this Column.
     */
    format : '0,000.00',
    //</locale>

    /**
     * @cfg {Object} renderer
     * @hide
     */
    
    /**
     * @cfg {Object} scope
     * @hide
     */

    defaultRenderer: function(value){
        return Ext.util.Format.number(value, this.format);
    }
});

/**
 * A special type of Grid {@link Ext.grid.column.Column} that provides automatic
 * row numbering.
 * 
 * Usage:
 *
 *     columns: [
 *         {xtype: 'rownumberer'},
 *         {text: "Company", flex: 1, sortable: true, dataIndex: 'company'},
 *         {text: "Price", width: 120, sortable: true, renderer: Ext.util.Format.usMoney, dataIndex: 'price'},
 *         {text: "Change", width: 120, sortable: true, dataIndex: 'change'},
 *         {text: "% Change", width: 120, sortable: true, dataIndex: 'pctChange'},
 *         {text: "Last Updated", width: 120, sortable: true, renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'}
 *     ]
 *
 */
Ext.define('Ext.grid.column.RowNumberer', {
    extend:  Ext.grid.column.Column ,
    alternateClassName: 'Ext.grid.RowNumberer',
    alias: 'widget.rownumberer',

    /**
     * @cfg {String} text
     * Any valid text or HTML fragment to display in the header cell for the row number column.
     */
    text: "&#160;",

    /**
     * @cfg {Number} width
     * The default width in pixels of the row number column.
     */
    width: 23,

    /**
     * @cfg {Boolean} sortable
     * @hide
     */
    sortable: false,
    
    /**
     * @cfg {Boolean} [draggable=false]
     * False to disable drag-drop reordering of this column.
     */
    draggable: false,

    // Flag to Lockable to move instances of this column to the locked side.
    autoLock: true,

    // May not be moved from its preferred locked side when grid is enableLocking:true
    lockable: false,

    align: 'right',

    constructor : function(config){
        var me = this;

        // Copy the prototype's default width setting into an instance property to provide
        // a default width which will not be overridden by AbstractContainer.applyDefaults use of Ext.applyIf
        me.width = me.width;

        me.callParent(arguments);
        me.scope = me;
    },

    beforeRender: function() {
        var rowBody = this.up('gridpanel').view.findFeature('rowbody');

        this.callParent(arguments);

        // If there is a RowBody Feature, and this coliumn is index 1 (immediately after the expander)...
        // the RowBody cell must not span this column, and this column must span into the expander row.
        if (rowBody && this.ownerCt.items.indexOf(this) === 1) {
            rowBody.colSpanDecrement = rowBody.colSpanDecrement + 1;
            this.rowspan = 2;
        }
    },

    // private
    resizable: false,
    hideable: false,
    menuDisabled: true,
    dataIndex: '',
    cls: Ext.baseCSSPrefix + 'row-numberer',
    tdCls: Ext.baseCSSPrefix + 'grid-cell-row-numberer ' + Ext.baseCSSPrefix + 'grid-cell-special',
    innerCls: Ext.baseCSSPrefix + 'grid-cell-inner-row-numberer',
    rowspan: undefined,

    // private
    renderer: function(value, metaData, record, rowIdx, colIdx, dataSource, view) {
        var rowspan = this.rowspan,
            page = dataSource.currentPage,
            result = view.store.indexOf(record);

        if (rowspan) {
            metaData.tdAttr = 'rowspan="' + rowspan + '"';
        }

        if (page > 1) {
            result += (page - 1) * dataSource.pageSize; 
        }
        return result + 1;
    }
});

/**
 * A Column definition class which renders a value by processing a {@link Ext.data.Model Model}'s
 * {@link Ext.data.Model#persistenceProperty data} using a {@link #tpl configured}
 * {@link Ext.XTemplate XTemplate}.
 * 
 *     @example
 *     Ext.create('Ext.data.Store', {
 *         storeId:'employeeStore',
 *         fields:['firstname', 'lastname', 'seniority', 'department'],
 *         groupField: 'department',
 *         data:[
 *             { firstname: "Michael", lastname: "Scott",   seniority: 7, department: "Management" },
 *             { firstname: "Dwight",  lastname: "Schrute", seniority: 2, department: "Sales" },
 *             { firstname: "Jim",     lastname: "Halpert", seniority: 3, department: "Sales" },
 *             { firstname: "Kevin",   lastname: "Malone",  seniority: 4, department: "Accounting" },
 *             { firstname: "Angela",  lastname: "Martin",  seniority: 5, department: "Accounting" }
 *         ]
 *     });
 *     
 *     Ext.create('Ext.grid.Panel', {
 *         title: 'Column Template Demo',
 *         store: Ext.data.StoreManager.lookup('employeeStore'),
 *         columns: [
 *             { text: 'Full Name',       xtype: 'templatecolumn', tpl: '{firstname} {lastname}', flex:1 },
 *             { text: 'Department (Yrs)', xtype: 'templatecolumn', tpl: '{department} ({seniority})' }
 *         ],
 *         height: 200,
 *         width: 300,
 *         renderTo: Ext.getBody()
 *     });
 */
Ext.define('Ext.grid.column.Template', {
    extend:  Ext.grid.column.Column ,
    alias: ['widget.templatecolumn'],
                                
    alternateClassName: 'Ext.grid.TemplateColumn',

    /**
     * @cfg {String/Ext.XTemplate} tpl
     * An {@link Ext.XTemplate XTemplate}, or an XTemplate *definition string* to use to process a
     * {@link Ext.data.Model Model}'s {@link Ext.data.Model#persistenceProperty data} to produce a
     * column's rendered value.
     */
    
    /**
     * @cfg {Object} renderer
     * @hide
     */
    
    /**
     * @cfg {Object} scope
     * @hide
     */

    initComponent: function(){
        var me = this;
        me.tpl = (!Ext.isPrimitive(me.tpl) && me.tpl.compile) ? me.tpl : new Ext.XTemplate(me.tpl);
        // Set this here since the template may access any record values,
        // so we must always run the update for this column
        me.hasCustomRenderer = true;
        me.callParent(arguments);
    },
    
    defaultRenderer: function(value, meta, record) {
        var data = Ext.apply({}, record.data, record.getAssociatedData());
        return this.tpl.apply(data);
    }
});

/**
 * A feature is a type of plugin that is specific to the {@link Ext.grid.Panel}. It provides several
 * hooks that allows the developer to inject additional functionality at certain points throughout the 
 * grid creation cycle. This class provides the base template methods that are available to the developer,
 * it should be extended.
 * 
 * There are several built in features that extend this class, for example:
 *
 *  - {@link Ext.grid.feature.Grouping} - Shows grid rows in groups as specified by the {@link Ext.data.Store}
 *  - {@link Ext.grid.feature.RowBody} - Adds a body section for each grid row that can contain markup.
 *  - {@link Ext.grid.feature.Summary} - Adds a summary row at the bottom of the grid with aggregate totals for a column.
 * 
 * ## Using Features
 * A feature is added to the grid by specifying it an array of features in the configuration:
 * 
 *     var groupingFeature = Ext.create('Ext.grid.feature.Grouping');
 *     Ext.create('Ext.grid.Panel', {
 *         // other options
 *         features: [groupingFeature]
 *     });
 *
 * ## Writing Features
 *
 * A Feature may add new DOM structure within the structure of a grid.
 *
 * A grid is essentially a `<table>` element. A {@link Ext.view.Table TableView} instance uses three {@link Ext.XTemplate XTemplates}
 * to render the grid, `tableTpl`, `rowTpl`, `cellTpl`.
 *
 * * A {@link Ext.view.Table TableView} uses its `tableTpl` to emit the `<table>` and `<tbody>` HTML tags into its output stream. It also emits a `<thead>` which contains a
 * sizing row. To ender the rows, it invokes {@link Ext.view.Table#renderRows} passing the `rows` member of its data object.
 *
 * The `tableTpl`'s data object Looks like this:
 *     {
 *         view: owningTableView,
 *         rows: recordsToRender,
 *         viewStartIndex: indexOfFirstRecordInStore,
 *         tableStyle: styleString
 *     }
 *
 * * A {@link Ext.view.Table TableView} uses its `rowTpl` to emit a `<tr>` HTML tag to its output stream. To render cells,
 * it invokes {@link Ext.view.Table#renderCell} passing the `rows` member of its data object.
 *
 * The `rowTpl`'s data object looks like this:
 *
 *     {
 *         view:        owningTableView,
 *         record:      recordToRender,
 *         recordIndex: indexOfRecordInStore,
 *         columns:     arrayOfColumnDefinitions,
 *         itemClasses: arrayOfClassNames, // For outermost row in case of wrapping
 *         rowClasses:  arrayOfClassNames,  // For internal data bearing row in case of wrapping
 *         rowStyle:    styleString
 *     }
 *
 * * A {@link Ext.view.Table TableView} uses its `cellTpl` to emit a `<td>` HTML tag to its output stream.
 *
 * The `cellTpl's` data object looks like this:
 *
 *     {
 *         record: recordToRender
 *         column: columnToRender;
 *         recordIndex: indexOfRecordInStore,
 *         columnIndex: columnIndex,
 *         align: columnAlign,
 *         tdCls: classForCell
 *     }
 *
 * A Feature may inject its own tableTpl or rowTpl or cellTpl into the {@link Ext.view.Table TableView}'s rendering by
 * calling {@link Ext.view.Table#addTableTpl} or {@link Ext.view.Table#addRowTpl} or {@link Ext.view.Table#addCellTpl}.
 *
 * The passed XTemplate is added *upstream* of the default template for the table element in a link list of XTemplates which contribute
 * to the element's HTML. It may emit appropriate HTML strings into the output stream *around* a call to
 *
 *     this.nextTpl.apply(values, out, parent);
 *
 * This passes the current value context, output stream and the parent value context to the next XTemplate in the list.
 *
 * @abstract
 */
Ext.define('Ext.grid.feature.Feature', {
    extend:  Ext.util.Observable ,
    alias: 'feature.feature',
    
    wrapsItem: false,

    /*
     * @property {Boolean} isFeature
     * `true` in this class to identify an object as an instantiated Feature, or subclass thereof.
     */
    isFeature: true,

    /**
     * True when feature is disabled.
     */
    disabled: false,

    /**
     * @property {Boolean}
     * Most features will expose additional events, some may not and will
     * need to change this to false.
     */
    hasFeatureEvent: true,

    /**
     * @property {String}
     * Prefix to use when firing events on the view.
     * For example a prefix of group would expose "groupclick", "groupcontextmenu", "groupdblclick".
     */
    eventPrefix: null,

    /**
     * @property {String}
     * Selector used to determine when to fire the event with the eventPrefix.
     */
    eventSelector: null,

    /**
     * @property {Ext.view.Table}
     * Reference to the TableView.
     */
    view: null,

    /**
     * @property {Ext.grid.Panel}
     * Reference to the grid panel
     */
    grid: null,
    
    constructor: function(config) {
        this.initialConfig = config;
        this.callParent(arguments);
    },

    clone: function() {
        return new this.self(this.initialConfig);
    },

    init: Ext.emptyFn,
    
    destroy: function(){
        this.clearListeners();
    },

    /**
     * Abstract method to be overriden when a feature should add additional
     * arguments to its event signature. By default the event will fire:
     *
     * - view - The underlying Ext.view.Table
     * - featureTarget - The matched element by the defined {@link #eventSelector}
     *
     * The method must also return the eventName as the first index of the array
     * to be passed to fireEvent.
     * @template
     */
    getFireEventArgs: function(eventName, view, featureTarget, e) {
        return [eventName, view, featureTarget, e];
    },
    
    vetoEvent: Ext.emptyFn,

    /**
     * Enables the feature.
     */
    enable: function() {
        this.disabled = false;
    },

    /**
     * Disables the feature.
     */
    disable: function() {
        this.disabled = true;
    }

});

/**
 * A small abstract class that contains the shared behaviour for any summary
 * calculations to be used in the grid.
 */
Ext.define('Ext.grid.feature.AbstractSummary', {

    extend:  Ext.grid.feature.Feature ,

    alias: 'feature.abstractsummary',

    summaryRowCls: Ext.baseCSSPrefix + 'grid-row-summary',
    summaryTableCls: Ext.plainTableCls + ' ' + Ext.baseCSSPrefix + 'grid-table',
    summaryRowSelector: '.' + Ext.baseCSSPrefix + 'grid-row-summary',

    // High priority rowTpl interceptor which sees summary rows early, and renders them correctly and then aborts the row rendering chain.
    // This will only see action when summary rows are being updated and Table.onUpdate->Table.bufferRender renders the individual updated sumary row.
    summaryRowTpl: {
        before: function(values, out) {
            // If a summary record comes through the rendering pipeline, render it simply, and return false from the
            // before method which aborts the tpl chain
            if (values.record.isSummary && this.summaryFeature.showSummaryRow) {
                this.summaryFeature.outputSummaryRecord(values.record, values, out);
                return false;
            }
        },
        priority: 1000
    },

   /**
    * @cfg {Boolean}
    * True to show the summary row.
    */
    showSummaryRow: true,

    // Listen for store updates. Eg, from an Editor.
    init: function() {
        var me = this;
        me.view.summaryFeature = me;
        me.rowTpl = me.view.self.prototype.rowTpl;

        // Add a high priority interceptor which renders summary records simply
        // This will only see action ona bufferedRender situation where summary records are updated.
        me.view.addRowTpl(me.summaryRowTpl).summaryFeature = me;
    },

    /**
     * Toggle whether or not to show the summary row.
     * @param {Boolean} visible True to show the summary row
     */
    toggleSummaryRow: function(visible) {
        this.showSummaryRow = !!visible;
    },

    outputSummaryRecord: function(summaryRecord, contextValues, out) {
        var view = contextValues.view,
            savedRowValues = view.rowValues,
            columns = contextValues.columns || view.headerCt.getVisibleGridColumns(),
            colCount = columns.length, i, column,
            // Set up a row rendering values object so that we can call the rowTpl directly to inject
            // the markup of a grid row into the output stream.
            values = {
                view: view,
                record: summaryRecord,
                rowStyle: '',
                rowClasses: [ this.summaryRowCls ],
                itemClasses: [],
                recordIndex: -1,
                rowId: view.getRowId(summaryRecord),
                columns: columns
            };

        // Because we are using the regular row rendering pathway, temporarily swap out the renderer for the summaryRenderer
        for (i = 0; i < colCount; i++) {
            column = columns[i];
            column.savedRenderer = column.renderer;
            if (column.summaryRenderer) {
                column.renderer = column.summaryRenderer;
            } else if (!column.summaryType) {
                column.renderer = Ext.emptyFn;
            }

            // Summary records may contain values based upon the column's ID if the column is not mapped from a field
            if (!column.dataIndex) {
                column.dataIndex = column.id;
            }
        }

        // Use the base template to render a summary row
        view.rowValues = values;
        view.self.prototype.rowTpl.applyOut(values, out);
        view.rowValues = savedRowValues;

        // Restore regular column renderers
        for (i = 0; i < colCount; i++) {
            column = columns[i];
            column.renderer = column.savedRenderer;
            column.savedRenderer = null;
        }
    },

    /**
     * Get the summary data for a field.
     * @private
     * @param {Ext.data.Store} store The store to get the data from
     * @param {String/Function} type The type of aggregation. If a function is specified it will
     * be passed to the stores aggregate function.
     * @param {String} field The field to aggregate on
     * @param {Boolean} group True to aggregate in grouped mode 
     * @return {Number/String/Object} See the return type for the store functions.
     * if the group parameter is `true` An object is returned with a property named for each group who's
     * value is the summary value.
     */
    getSummary: function(store, type, field, group){
        var records = group.records;

        if (type) {
            if (Ext.isFunction(type)) {
                return store.getAggregate(type, null, records, [field]);
            }

            switch (type) {
                case 'count':
                    return records.length;
                case 'min':
                    return store.getMin(records, field);
                case 'max':
                    return store.getMax(records, field);
                case 'sum':
                    return store.getSum(records, field);
                case 'average':
                    return store.getAverage(records, field);
                default:
                    return '';

            }
        }
    },

    /**
     * Used by the Grouping Feature when {@link #showSummaryRow} is `true`.
     * 
     * Generates group summary data for the whole store.
     * @private
     * @return {Object} An object hash keyed by group name containing summary records.
     */
    generateSummaryData: function(){
        var me = this,
            store = me.view.store,
            groups = store.groups.items,
            reader = store.proxy.reader,
            len = groups.length,
            groupField = me.getGroupField(),
            data = {},
            lockingPartner = me.lockingPartner,
            i, group, record,
            root, summaryRows, hasRemote,
            convertedSummaryRow, remoteData;

        /**
         * @cfg {String} [remoteRoot=undefined]
         * The name of the property which contains the Array of summary objects.
         * It allows to use server-side calculated summaries.
         */
        if (me.remoteRoot && reader.rawData) {
            hasRemote = true;
            remoteData = {};
            // reset reader root and rebuild extractors to extract summaries data
            root = reader.root;
            reader.root = me.remoteRoot;
            reader.buildExtractors(true);
            summaryRows = reader.getRoot(reader.rawData)||[];
            len = summaryRows.length;

            // Ensure the Reader has a data conversion function to convert a raw data row into a Record data hash
            if (!reader.convertRecordData) {
                reader.buildExtractors();
            }

            for (i = 0; i < len; ++i) {
                convertedSummaryRow = {};

                // Convert a raw data row into a Record's hash object using the Reader
                reader.convertRecordData(convertedSummaryRow, summaryRows[i]);
                remoteData[convertedSummaryRow[groupField]] = convertedSummaryRow;
            }

            // restore initial reader configuration
            reader.root = root;
            reader.buildExtractors(true);
        }

        for (i = 0; i < len; ++i) {
            group = groups[i];
            // Something has changed or it doesn't exist, populate it
            if (hasRemote || group.isDirty() || !group.hasAggregate()) {
                if (hasRemote) {
                    record = me.populateRemoteRecord(group, remoteData);
                } else {
                    record = me.populateRecord(group);
                }
                // Clear the dirty state of the group if this is the only Summary, or this is the right hand (normal grid's) summary
                if (!lockingPartner || (me.view.ownerCt === me.view.ownerCt.ownerLockable.normalGrid)) {
                    group.commit();
                }
            } else {
                record = group.getAggregateRecord();
            }
            data[group.key] = record;
        }

        return data;
    },

    populateRemoteRecord: function(group, data) {
        var record = group.getAggregateRecord(true),
            groupData = data[group.key],
            field;

        record.beginEdit();
        for (field in groupData) {
            if (groupData.hasOwnProperty(field)) {
                if (field !== record.idProperty) {
                    record.set(field, groupData[field]);
                }
            }
        }
        record.endEdit(true);
        record.commit(true);

        return record;
    },

    populateRecord: function(group){
        var me = this,
            view = me.grid.ownerLockable ? me.grid.ownerLockable.view : me.view,
            store = me.view.store,
            record = group.getAggregateRecord(),
            // Use the full column set, regardless of locking
            columns = view.headerCt.getGridColumns(),
            len = columns.length,
            i, column, fieldName;

        record.beginEdit();
        for (i = 0; i < len; ++i) {
            column = columns[i];
            // Use the column id if there's no mapping, could be a calculated field
            fieldName = column.dataIndex || column.id;
            record.set(fieldName, me.getSummary(store, column.summaryType, fieldName, group));
        }
        record.endEdit(true);
        record.commit();

        return record;
    }
});

/**
 * Private record store class which takes the place of the view's data store to provide a grouped
 * view of the data when the Grouping feature is used.
 * 
 * Relays granular mutation events from the underlying store as refresh events to the view.
 * 
 * On mutation events from the underlying store, updates the summary rows by firing update events on the corresponding
 * summary records.
 * @private
 */
Ext.define('Ext.grid.feature.GroupStore', {
    extend:  Ext.util.Observable ,

    isStore: true,

    // Number of records to load into a buffered grid before it has been bound to a view of known size
    defaultViewSize: 100,

    // Use this property moving forward for all feature stores. It will be used to ensure
    // that the correct object is used to call various APIs. See EXTJSIV-10022.
    isFeatureStore: true,

    constructor: function(groupingFeature, store) {
        var me = this;

        me.callParent();
        me.groupingFeature = groupingFeature;
        me.bindStore(store);
    },

    bindStore: function(store) {
        var me = this;

        if (me.store) {
            Ext.destroy(me.storeListeners);
            me.store = null;
        }
        if (store) {
            me.storeListeners = store.on({
                bulkremove: me.onBulkRemove,
                add: me.onAdd,
                update: me.onUpdate,
                refresh: me.onRefresh,
                clear: me.onClear,
                scope: me,
                destroyable: true
            });
            me.store = store;
            me.processStore(store);
        }
    },

    processStore: function(store) {
        var me = this,
            groups = store.getGroups(),
            groupCount = groups.length,
            i,
            group,
            groupPlaceholder,
            data = me.data,
            oldGroupCache = me.groupingFeature.groupCache,
            groupCache = me.groupingFeature.clearGroupCache(),
            collapseAll = me.groupingFeature.startCollapsed;

        if (data) {
            data.clear();
        } else {
            data = me.data = new Ext.util.MixedCollection(false, Ext.data.Store.recordIdFn);
        }

        if (store.getCount()) {

            // Upon first process of a loaded store, clear the "always" collapse" flag
            me.groupingFeature.startCollapsed = false;

            for (i = 0; i < groupCount; i++) {

                
                
                group = groups[i];

                
                groupCache[group.name] = group;
                group.isCollapsed = collapseAll || (oldGroupCache[group.name] && oldGroupCache[group.name].isCollapsed);

                
                
                if (group.isCollapsed) {
                    group.placeholder = groupPlaceholder = new store.model(null, 'group-' + group.name + '-placeholder');
                    groupPlaceholder.set(store.getGroupField(), group.name);
                    groupPlaceholder.rows = groupPlaceholder.children = group.children;
                    groupPlaceholder.isCollapsedPlaceholder = true;
                    data.add(groupPlaceholder);
                }

                
                else {
                    data.insert(me.data.length, group.children);
                }
            }
        }
    },

    isCollapsed: function(name) {
        return this.groupingFeature.groupCache[name].isCollapsed; 
    },

    isInCollapsedGroup: function(record) {
        var store = this.store,
            groupData;

        if (store.isGrouped() && (groupData = this.groupingFeature.groupCache[record.get(store.getGroupField())])) {
            return groupData.isCollapsed || false;
        }
        return false;
    },

    getCount: function() {
        return this.data.getCount();
    },

    getTotalCount: function() {
        return this.data.getCount();
    },

    
    rangeCached: function(start, end) {
        return end < this.getCount();
    },

    getRange: function(start, end, options) {
        var result = this.data.getRange(start, end);

        if (options && options.callback) {
            options.callback.call(options.scope || this, result, start, end, options);
        }
        return result;
    },

    getAt: function(index) {
        return this.getRange(index, index)[0];
    },

    getById: function(id) {
        return this.store.getById(id);
    },

    expandGroup: function(group) {
        var me = this,
            startIdx;

        if (typeof group === 'string') {
            group = me.groupingFeature.groupCache[group];
        }

        if (group && group.children.length && (startIdx = me.indexOf(group.children[0], true, true)) !== -1) {

            
            group.isCollapsed = false;
            me.isExpandingOrCollapsing = 1;
            
            
            me.data.removeAt(startIdx);
            me.fireEvent('bulkremove', me, [me.getGroupPlaceholder(group)], [startIdx]);

            
            me.data.insert(startIdx, group.children);
            me.fireEvent('add', me, group.children, startIdx);

            me.fireEvent('groupexpand', me, group);
            me.isExpandingOrCollapsing = 0;
        }
    },

    collapseGroup: function(group) {
        var me = this,
            startIdx,
            placeholder,
            i, j, len,
            removeIndices;

        if (typeof group === 'string') {
            group = me.groupingFeature.groupCache[group];
        }

        if (group && (len = group.children.length) && (startIdx = me.indexOf(group.children[0], true)) !== -1) {

            
            group.isCollapsed = true;
            me.isExpandingOrCollapsing = 2;

            
            me.data.removeRange(startIdx, len);

            
            removeIndices = new Array(len);
            for (i = 0, j = startIdx; i < len; i++, j++) {
                removeIndices[i] = j;
            }
            me.fireEvent('bulkremove', me, group.children, removeIndices);

            
            me.data.insert(startIdx, placeholder = me.getGroupPlaceholder(group));
            me.fireEvent('add', me, [placeholder], startIdx);

            me.fireEvent('groupcollapse', me, group);
            me.isExpandingOrCollapsing = 0;
        }
    },

    getGroupPlaceholder: function(group) {
        if (!group.placeholder) {
            var groupPlaceholder = group.placeholder = new this.store.model(null, 'group-' + group.name + '-placeholder');
            groupPlaceholder.set(this.store.getGroupField(), group.name);
            groupPlaceholder.rows = groupPlaceholder.children = group.children;
            groupPlaceholder.isCollapsedPlaceholder = true;
        }
        return group.placeholder;
    },

    
    
    
    indexOf: function(record, viewOnly, includeCollapsed) {
        var me = this,
            groups,
            groupCount,
            i,
            group,
            groupIndex,
            result = 0;

        if (record && (includeCollapsed || !me.isInCollapsedGroup(record))) {
            groups = me.store.getGroups();
            groupCount = groups.length;
            for (i = 0; i < groupCount; i++) {

                
                
                group = groups[i];
                if (group.name === this.store.getGroupString(record)) {
                    groupIndex = Ext.Array.indexOf(group.children, record);
                    return result + groupIndex;
                }

                result += (viewOnly && me.isCollapsed(group.name)) ? 1 : group.children.length;
            }
        }
        return -1;
    },

    
    indexOfTotal: function(record) {
        return this.store.indexOf(record);
    },

    onRefresh: function(store) {
        this.processStore(this.store);
        this.fireEvent('refresh', this);
    },

    onBulkRemove: function(store, records, indices) {
        this.processStore(this.store);
        this.fireEvent('refresh', this);
    },

    onClear: function(store, records, startIndex) {
        this.processStore(this.store);
        this.fireEvent('clear', this);
    },

    onAdd: function(store, records, startIndex) {
        this.processStore(this.store);
        this.fireEvent('refresh', this);
    },

    onUpdate: function(store, record, operation, modifiedFieldNames) {
        var me = this,
            groupInfo,
            firstRec, lastRec;

        
        
        
        if (store.isGrouped()) {
            
            groupInfo = record.group = me.groupingFeature.getRecordGroup(record);

            if (modifiedFieldNames && Ext.Array.contains(modifiedFieldNames, me.groupingFeature.getGroupField())) {
                return me.onRefresh(me.store);
            }

            
            if (groupInfo.isCollapsed) {
                me.fireEvent('update', me, groupInfo.placeholder);
            }

            
            
            else {
                Ext.suspendLayouts();

                
                me.fireEvent('update', me, record, operation, modifiedFieldNames);

                
                
                firstRec = groupInfo.children[0];
                lastRec = groupInfo.children[groupInfo.children.length - 1];

                
                if (firstRec !== record) {
                    firstRec.group = groupInfo;
                    me.fireEvent('update', me, firstRec, 'edit');
                    delete firstRec.group;
                }
                if (lastRec !== record && lastRec !== firstRec && me.groupingFeature.showSummaryRow) {
                    lastRec.group = groupInfo;
                    me.fireEvent('update', me, lastRec, 'edit');
                    delete lastRec.group;
                }
                Ext.resumeLayouts(true);
            }

            delete record.group;
        } else {
            
            me.fireEvent('update', me, record, operation, modifiedFieldNames);
        }
    }
});


Ext.define('Ext.grid.feature.Grouping', {
    extend:  Ext.grid.feature.Feature ,
    mixins: {
        summary:  Ext.grid.feature.AbstractSummary 
    },
                                              

    alias: 'feature.grouping',

    eventPrefix: 'group',
    groupCls: Ext.baseCSSPrefix + 'grid-group-hd',
    eventSelector: '.' + Ext.baseCSSPrefix + 'grid-group-hd',

    refreshData: {},
    groupInfo: {},
    wrapsItem: true,

    

    

    

    

    

    
    groupHeaderTpl: '{columnName}: {name}',

    
    depthToIndent: 17,

    collapsedCls: Ext.baseCSSPrefix + 'grid-group-collapsed',
    hdCollapsedCls: Ext.baseCSSPrefix + 'grid-group-hd-collapsed',
    hdNotCollapsibleCls: Ext.baseCSSPrefix + 'grid-group-hd-not-collapsible',
    collapsibleCls: Ext.baseCSSPrefix + 'grid-group-hd-collapsible',
    ctCls: Ext.baseCSSPrefix  + 'group-hd-container',

    
    
    groupByText : 'Group by this field',
    
    
    
    showGroupsText : 'Show in groups',
    

    
    hideGroupedHeader : false,

    
    startCollapsed : false,

    
    enableGroupingMenu : true,

    
    enableNoGroups : true,

    
    collapsible: true,

    
    expandTip: 'Click to expand. CTRL key collapses all others',
    

    
    collapseTip: 'Click to collapse. CTRL/click collapses all others',
    

    showSummaryRow: false,

    tableTpl: {
        before: function(values) {
            
            if (this.groupingFeature.disabled || values.rows.length === 1 && values.rows[0].isSummary) {
                return;
            }
            this.groupingFeature.setup(values.rows, values.view.rowValues);
        },
        after: function(values) {
            
            if (this.groupingFeature.disabled || values.rows.length === 1 && values.rows[0].isSummary) {
                return;
            }
            this.groupingFeature.cleanup(values.rows, values.view.rowValues);
        },
        priority: 200
    },

    groupTpl: [
        '{%',
            'var me = this.groupingFeature;',
            
            'if (me.disabled) {',
                'values.needsWrap = false;',
            '} else {',
                
                'me.setupRowData(values.record, values.rowIndex, values);',
            '}',
        '%}',
        '<tpl if="needsWrap">',
            '<tr {[values.isCollapsedGroup ? ("id=\\"" + values.rowId + "\\"") : ""]} data-boundView="{view.id}" data-recordId="{record.internalId:htmlEncode}" data-recordIndex="{[values.isCollapsedGroup ? -1 : values.recordIndex]}" ',
                'class="{[values.itemClasses.join(" ")]} ', Ext.baseCSSPrefix, 'grid-wrap-row<tpl if="!summaryRecord"> ', Ext.baseCSSPrefix, 'grid-group-row</tpl>" {ariaRowAttr}>',
                '<td class="', Ext.baseCSSPrefix, 'group-hd-container" colspan="{columns.length}" {ariaCellAttr}>',
                    '<tpl if="isFirstRow">',
                        '{%',
                            
                            
                            'var groupTitleStyle = (!values.view.lockingPartner || (values.view.ownerCt === values.view.ownerCt.ownerLockable.lockedGrid) || (values.view.lockingPartner.headerCt.getVisibleGridColumns().length === 0)) ? "" : "visibility:hidden";',
                        '%}',
                        '<div id="{groupId}" class="', Ext.baseCSSPrefix, 'grid-group-hd {collapsibleCls}" tabIndex="0" hidefocus="on" {ariaCellInnerAttr}>',
                            '<div class="', Ext.baseCSSPrefix, 'grid-group-title" style="{[groupTitleStyle]}" {ariaGroupTitleAttr}>',
                                '{[values.groupHeaderTpl.apply(values.groupInfo, parent) || "&#160;"]}',
                            '</div>',
                        '</div>',
                    '</tpl>',

                    
                    '<tpl if="summaryRecord || !isCollapsedGroup">',
                        '<table class="', Ext.baseCSSPrefix, '{view.id}-table ', Ext.baseCSSPrefix, 'grid-table',
                            '<tpl if="summaryRecord"> ', Ext.baseCSSPrefix, 'grid-table-summary</tpl>"',
                            'border="0" cellspacing="0" cellpadding="0" style="width:100%" {ariaSummaryTableAttr}>',
                            '{[values.view.renderColumnSizer(out)]}',
                            
                            '<tpl if="!isCollapsedGroup">',
                                '{%',
                                    'values.itemClasses.length = 0;',
                                    'this.nextTpl.applyOut(values, out, parent);',
                                '%}',
                            '</tpl>',
                            '<tpl if="summaryRecord">',
                                '{%me.outputSummaryRecord(values.summaryRecord, values, out);%}',
                            '</tpl>',
                        '</table>',
                    '</tpl>',
                '</td>',
            '</tr>',
        '<tpl else>',
            '{%this.nextTpl.applyOut(values, out, parent);%}',
        '</tpl>', {
            priority: 200,

            syncRowHeights: function(firstRow, secondRow) {
                firstRow = Ext.fly(firstRow, 'syncDest');
                secondRow = Ext.fly(secondRow, 'sycSrc');
                var owner = this.owner,
                    firstHd = firstRow.down(owner.eventSelector, true),
                    secondHd,
                    firstSummaryRow = firstRow.down(owner.summaryRowSelector, true),
                    secondSummaryRow,
                    firstHeight, secondHeight;

                
                if (firstHd && (secondHd = secondRow.down(owner.eventSelector, true))) {
                    firstHd.style.height = secondHd.style.height = '';
                    if ((firstHeight = firstHd.offsetHeight) > (secondHeight = secondHd.offsetHeight)) {
                        Ext.fly(secondHd).setHeight(firstHeight);
                    }
                    else if (secondHeight > firstHeight) {
                        Ext.fly(firstHd).setHeight(secondHeight);
                    }
                }

                
                if (firstSummaryRow && (secondSummaryRow = secondRow.down(owner.summaryRowSelector, true))) {
                    firstSummaryRow.style.height = secondSummaryRow.style.height = '';
                    if ((firstHeight = firstSummaryRow.offsetHeight) > (secondHeight = secondSummaryRow.offsetHeight)) {
                        Ext.fly(secondSummaryRow).setHeight(firstHeight);
                    }
                    else if (secondHeight > firstHeight) {
                        Ext.fly(firstSummaryRow).setHeight(secondHeight);
                    }
                }
            },

            syncContent: function(destRow, sourceRow) {
                destRow = Ext.fly(destRow, 'syncDest');
                sourceRow = Ext.fly(sourceRow, 'sycSrc');
                var owner = this.owner,
                    destHd = destRow.down(owner.eventSelector, true),
                    sourceHd = sourceRow.down(owner.eventSelector, true),
                    destSummaryRow = destRow.down(owner.summaryRowSelector, true),
                    sourceSummaryRow = sourceRow.down(owner.summaryRowSelector, true);

                
                if (destHd && sourceHd) {
                    Ext.fly(destHd).syncContent(sourceHd);
                }

                
                if (destSummaryRow && sourceSummaryRow) {
                    Ext.fly(destSummaryRow).syncContent(sourceSummaryRow);
                }
            }
        }
    ],

    constructor: function() {
        this.groupCache = {};
        this.callParent(arguments);
    },

    init: function(grid) {
        var me = this,
            view = me.view;

        if (view.store.groupField) {
            view.isGrouping = true;
        }

        
        if (me.lockingPartner && me.lockingPartner.groupCache) {
            me.groupCache = me.lockingPartner.groupCache;
        }

        me.mixins.summary.init.call(me);

        me.callParent(arguments);
        view.headerCt.on({
            columnhide: me.onColumnHideShow,
            columnshow: me.onColumnHideShow,
            columnmove: me.onColumnMove,
            scope: me
        });

        
        view.addTableTpl(me.tableTpl).groupingFeature = me;

        
        view.addRowTpl(Ext.XTemplate.getTpl(me, 'groupTpl')).groupingFeature = me;

        view.preserveScrollOnRefresh = true;

        
        if (view.store.buffered) {
            me.collapsible = false;
        }
        
        else {

            
            if (this.lockingPartner && this.lockingPartner.dataSource) {
                me.dataSource = view.dataSource = this.lockingPartner.dataSource;
            } else {
                me.dataSource = view.dataSource = new Ext.grid.feature.GroupStore(me, view.store);
            }
        }

        me.grid.on({
            reconfigure: me.onReconfigure
        });
        view.on({
            afterrender: me.afterViewRender,
            scope: me,
            single: true
        });
    },

    indexOf: function(record) {
        var me = this,
            store = me.view.store,
            recordIdx = record.isModel ? store.indexOf(record) : Math.min(Math.max(record, 0), store.getCount() - 1),
            storeGroups,
            i, length, storeGroup, myGroup, groupStart,
            nextGroupStart = 0,
            skippedRecords = 0;

        storeGroups = store.getGroups();

        
        for (i = 0, length = storeGroups.length; i < length; i++) {
            storeGroup = storeGroups[i];
            myGroup = me.getGroup(storeGroup.name);

            groupStart = nextGroupStart;

            
            nextGroupStart = groupStart + storeGroup.children.length;

            
            if (recordIdx < nextGroupStart) {
                return recordIdx - skippedRecords;
            }

            
            
            if (myGroup.isCollapsed) {
                skippedRecords += storeGroup.children.length;
                skippedRecords -= 1;
            }
        }
    },

    isInCollapsedGroup: function(record) {
        var groupData,
            store = this.view.store;

        if (store.isGrouped() && (groupData = this.getGroup(record))) {
            return groupData.isCollapsed || false;
        }
        return false;
    },

    clearGroupCache: function() {
        var me = this,
            groupCache = me.groupCache = {};

        if (me.lockingPartner) {
            me.lockingPartner.groupCache = groupCache;
        }
        return groupCache;
    },

    vetoEvent: function(record, row, rowIndex, e) {
        
        if (e.type !== 'mouseover' && e.type !== 'mouseout'  && e.type !== 'mouseenter' && e.type !== 'mouseleave' && e.getTarget(this.eventSelector)) {
            return false;
        }
    },

    enable: function() {
        var me    = this,
            view  = me.view,
            store = view.store,
            groupToggleMenuItem;

        view.isGrouping = true;
        if (me.lastGroupers) {
            me.block();
            store.group(me.lastGroupers);
            me.lastGroupers = null;
            me.unblock();
        }
        me.callParent();
        groupToggleMenuItem = me.view.headerCt.getMenu().down('#groupToggleMenuItem');
        if (groupToggleMenuItem) {
            groupToggleMenuItem.setChecked(true, true);
        }
        me.refreshIf();
    },

    disable: function() {
        var me    = this,
            view  = me.view,
            store = view.store,
            groupToggleMenuItem,
            lastGroupers = store.groupers.getRange();

        view.isGrouping = false;
        if (lastGroupers.length) {
            me.lastGroupers = lastGroupers;
            me.block();
            store.clearGrouping();
            me.unblock();
        }

        me.callParent();
        groupToggleMenuItem = me.view.headerCt.getMenu().down('#groupToggleMenuItem');
        if (groupToggleMenuItem) {
            groupToggleMenuItem.setChecked(false, true);
        }
        me.refreshIf();
    },

    refreshIf: function() {
        var ownerCt = this.grid.ownerCt,
            view = this.view;

        if (!view.store.remoteGroup && !this.blockRefresh) {

            
            if (ownerCt && ownerCt.lockable) {
                ownerCt.view.refresh();
            } else {
                view.refresh();
            }
        }
    },

    
    afterViewRender: function() {
        var me = this,
            view = me.view;

        view.on({
            scope: me,
            groupclick: me.onGroupClick
        });

        if (me.enableGroupingMenu) {
            me.injectGroupingMenu();
        }

        me.pruneGroupedHeader();

        me.lastGroupers = me.view.store.groupers.getRange();
        me.block();
        me.onGroupChange();
        me.unblock();

        
        
        if (me.disabled) {
            me.disable();
        }

    },

    injectGroupingMenu: function() {
        var me       = this,
            headerCt = me.view.headerCt;

        headerCt.showMenuBy = me.showMenuBy;
        headerCt.getMenuItems = me.getMenuItems();
    },

    onColumnHideShow: function(headerOwnerCt, header) {
        var view = this.view,
            headerCt = view.headerCt,
            menu = headerCt.getMenu(),
            groupToggleMenuItem  = menu.down('#groupMenuItem'),
            colCount = this.grid.getVisibleColumnManager().getColumns().length,
            items,
            len,
            i;

        
        if (groupToggleMenuItem) {
            if (headerCt.getVisibleGridColumns().length > 1) {
                groupToggleMenuItem.enable();
            } else {
                groupToggleMenuItem.disable();
            }
        }

        
        if (view.rendered) {
            items = view.el.query('.' + this.ctCls);
            for (i = 0, len = items.length; i < len; ++i) {
                items[i].colSpan = colCount;
            }
        }
    },

    
    
    onColumnMove: function() {
        var me = this,
            store = me.view.store,
            groups,
            groupName,
            group, firstRec, lastRec;

        if (store.isGrouped()) {
            groups = me.groupCache;
            for (groupName in groups) {
                if (groups.hasOwnProperty(groupName)) {
                    group = groups[groupName];
                    firstRec = group.children[0];
                    lastRec = group.children[group.children.length - 1];

                    
                    
                    store.fireEvent('update', store, firstRec, 'edit', null);
                    if (lastRec !== firstRec && me.showSummaryRow) {
                        store.fireEvent('update', store, lastRec, 'edit', null);
                    }
                }
            }
        }
    },

    showMenuBy: function(t, header) {
        var menu = this.getMenu(),
            groupMenuItem  = menu.down('#groupMenuItem'),
            groupMenuMeth = header.groupable === false || header.dataIndex == null || this.view.headerCt.getVisibleGridColumns().length < 2 ?  'disable' : 'enable',
            groupToggleMenuItem  = menu.down('#groupToggleMenuItem'),
            isGrouped = this.view.store.isGrouped();

        groupMenuItem[groupMenuMeth]();
        if (groupToggleMenuItem) {
            groupToggleMenuItem.setChecked(isGrouped, true);
            groupToggleMenuItem[isGrouped ?  'enable' : 'disable']();
        }
        Ext.grid.header.Container.prototype.showMenuBy.apply(this, arguments);
    },

    getMenuItems: function() {
        var me                 = this,
            groupByText        = me.groupByText,
            disabled           = me.disabled || !me.getGroupField(),
            showGroupsText     = me.showGroupsText,
            enableNoGroups     = me.enableNoGroups,
            getMenuItems       = me.view.headerCt.getMenuItems;

        
        return function() {

            
            
            var o = getMenuItems.call(this);
            o.push('-', {
                iconCls: Ext.baseCSSPrefix + 'group-by-icon',
                itemId: 'groupMenuItem',
                text: groupByText,
                handler: me.onGroupMenuItemClick,
                scope: me
            });
            if (enableNoGroups) {
                o.push({
                    itemId: 'groupToggleMenuItem',
                    text: showGroupsText,
                    checked: !disabled,
                    checkHandler: me.onGroupToggleMenuItemClick,
                    scope: me
                });
            }
            return o;
        };
    },

    
    onGroupMenuItemClick: function(menuItem, e) {
        var me = this,
            menu = menuItem.parentMenu,
            hdr  = menu.activeHeader,
            view = me.view,
            store = view.store;

        me.lastGroupers = null;
        me.block();
        me.enable();
        store.group(hdr.dataIndex);
        me.pruneGroupedHeader();
        me.unblock();
        me.refreshIf();
    },

    block: function(fromPartner) {
        this.blockRefresh = this.view.blockRefresh = true;
        if (this.lockingPartner && !fromPartner) {
            this.lockingPartner.block(true);
        }
    },

    unblock: function(fromPartner) {
        this.blockRefresh = this.view.blockRefresh = false;
        if (this.lockingPartner && !fromPartner) {
            this.lockingPartner.unblock(true);
        }
    },

    
    onGroupToggleMenuItemClick: function(menuItem, checked) {
        this[checked ? 'enable' : 'disable']();
    },

    
    pruneGroupedHeader: function() {
        var me = this,
            header = me.getGroupedHeader();

        if (me.hideGroupedHeader && header) {
            Ext.suspendLayouts();
            if (me.prunedHeader && me.prunedHeader !== header) {
                me.prunedHeader.show();
            }
            me.prunedHeader = header;
            header.hide();
            Ext.resumeLayouts(true);
        }
    },

    getHeaderNode: function(groupName) {
        return Ext.get(this.createGroupId(groupName));
    },

    getGroup: function(name) {
        if (name.isModel) {
            name = name.get(this.view.store.getGroupField());
        }
        var cache = this.groupCache,
            item = cache[name];

        if (!item) {
            item = cache[name] = {
                isCollapsed: false
            };
        }
        return item;
    },

    
    isExpanded: function(groupName) {
        return !this.getGroup(groupName).isCollapsed;
    },

    
    expand: function(groupName, focus) {
        this.doCollapseExpand(false, groupName, focus);
    },

    
    expandAll: function() {
        var me = this,
            groupCache = me.groupCache,
            groupName,
            lockingPartner = me.lockingPartner;

        
        
        for (groupName in groupCache) {
            if (groupCache.hasOwnProperty(groupName)) {
                groupCache[groupName].isCollapsed = false;
            }
        }
        Ext.suspendLayouts();
        me.dataSource.onRefresh();
        Ext.resumeLayouts(true);

        
        for (groupName in groupCache) {
            if (groupCache.hasOwnProperty(groupName)) {
                me.afterCollapseExpand(false, groupName);
                if (lockingPartner) {
                    lockingPartner.afterCollapseExpand(false, groupName);
                }
            }
        }
    },

    
    collapse: function(groupName, focus) {
        this.doCollapseExpand(true, groupName, focus);
    },

    
    
    isAllCollapsed: function() {
        var me = this,
            groupCache = me.groupCache,
            groupName;

        
        
        for (groupName in groupCache) {
            if (groupCache.hasOwnProperty(groupName)) {
                if (!groupCache[groupName].isCollapsed) {
                    return false;
                }
            }
        }
        return true;
    },

    
    
    isAllExpanded: function() {
        var me = this,
            groupCache = me.groupCache,
            groupName;

        
        
        for (groupName in groupCache) {
            if (groupCache.hasOwnProperty(groupName)) {
                if (groupCache[groupName].isCollapsed) {
                    return false;
                }
            }
        }
        return true;
    },

    
    collapseAll: function() {
        var me = this,
            groupCache = me.groupCache,
            groupName,
            lockingPartner = me.lockingPartner;

        
        
        for (groupName in groupCache) {
            if (groupCache.hasOwnProperty(groupName)) {
                groupCache[groupName].isCollapsed = true;
            }
        }
        Ext.suspendLayouts();
        me.dataSource.onRefresh();

        if (lockingPartner && !lockingPartner.isAllCollapsed()) {
            lockingPartner.collapseAll();
        }
        Ext.resumeLayouts(true);

        
        for (groupName in groupCache) {
            if (groupCache.hasOwnProperty(groupName)) {
                me.afterCollapseExpand(true, groupName);
                if (lockingPartner) {
                    lockingPartner.afterCollapseExpand(true, groupName);
                }
            }
        }

    },

    doCollapseExpand: function(collapsed, groupName, focus) {
        var me = this,
            lockingPartner = me.lockingPartner,
            group = me.groupCache[groupName];

        
        if (group.isCollapsed != collapsed) {

            
            
            Ext.suspendLayouts();
            if (collapsed) {
                me.dataSource.collapseGroup(group);
            } else {
                me.dataSource.expandGroup(group);
            }
            Ext.resumeLayouts(true);

            
            me.afterCollapseExpand(collapsed, groupName, focus);

            
            
            if (lockingPartner) {
                lockingPartner.afterCollapseExpand(collapsed, groupName, false);
            }
        }
    },

    afterCollapseExpand: function(collapsed, groupName, focus) {
        var me = this,
            view = me.view,
            header;

        header = Ext.get(this.getHeaderNode(groupName));
        view.fireEvent(collapsed ? 'groupcollapse' : 'groupexpand', view, header, groupName);
        if (focus) {
            header.up(view.getItemSelector()).scrollIntoView(view.el, null, true);
        }
    },

    onGroupChange: function() {
        var me = this,
            field = me.getGroupField(),
            menuItem,
            visibleGridColumns,
            groupingByLastVisibleColumn;

        if (me.hideGroupedHeader) {
            if (me.lastGroupers && me.lastGroupers.length) {
                menuItem = me.getMenuItem(me.lastGroupers[0].property);
                if (menuItem) {
                    menuItem.setChecked(true);
                }
            }
            if (field) {
                visibleGridColumns = me.view.headerCt.getVisibleGridColumns();

                
                
                groupingByLastVisibleColumn = ((visibleGridColumns.length === 1) && (visibleGridColumns[0].dataIndex == field));
                menuItem = me.getMenuItem(field);
                if (menuItem && !groupingByLastVisibleColumn) {
                    menuItem.setChecked(false);
                }
            }
        }
        me.refreshIf();
        me.lastGroupers = me.view.store.groupers.getRange();
    },

    
    getMenuItem: function(dataIndex){
        var view = this.view,
            header = view.headerCt.down('gridcolumn[dataIndex=' + dataIndex + ']'),
            menu = view.headerCt.getMenu();

        return header ? menu.down('menuitem[headerId='+ header.id +']') : null;
    },

    onGroupKey: function(keyCode, event) {
        var me = this,
            groupName = me.getGroupName(event.target);

        if (groupName) {
            me.onGroupClick(me.view, event.target, groupName, event);
        }
    },

    
    onGroupClick: function(view, rowElement, groupName, e) {
        var me = this,
            groupCache = me.groupCache,
            groupIsCollapsed = !me.isExpanded(groupName),
            g;

        if (me.collapsible) {

            
            if (e.ctrlKey) {
                Ext.suspendLayouts();
                for (g in groupCache) {
                    if (g === groupName) {
                        if (groupIsCollapsed) {
                            me.expand(groupName);
                        }
                    } else {
                        me.doCollapseExpand(true, g, false);
                    }
                }
                Ext.resumeLayouts(true);
                return;
            }

            if (groupIsCollapsed) {
               me.expand(groupName);
            } else {
                me.collapse(groupName);
            }
        }
    },

    setupRowData: function(record, idx, rowValues) {
        var me = this,
            data = me.refreshData,
            groupInfo = me.groupInfo,
            header = data.header,
            groupField = data.groupField,
            dataSource = me.view.dataSource,
            grouper, groupName, prev, next;

        rowValues.isCollapsedGroup = false;
        rowValues.summaryRecord = null;

        if (data.doGrouping) {
            grouper = me.view.store.groupers.first();

            
            
            if (record.children) {
                groupName = grouper.getGroupString(record.children[0]);

                rowValues.isFirstRow = rowValues.isLastRow = true;
                rowValues.itemClasses.push(me.hdCollapsedCls);
                rowValues.isCollapsedGroup = rowValues.needsWrap = true;
                rowValues.groupInfo = groupInfo;
                groupInfo.groupField = groupField;
                groupInfo.name = groupName;
                groupInfo.groupValue = record.children[0].get(groupField);
                groupInfo.columnName = header ? header.text : groupField;
                rowValues.collapsibleCls = me.collapsible ? me.collapsibleCls : me.hdNotCollapsibleCls;
                rowValues.groupId = me.createGroupId(groupName);
                groupInfo.rows = groupInfo.children = record.children;
                if (me.showSummaryRow) {
                    rowValues.summaryRecord = data.summaryData[groupName];
                }
                return;
            }

            groupName = grouper.getGroupString(record);

            
            if (record.group) {
                rowValues.isFirstRow = record === record.group.children[0];
                rowValues.isLastRow  = record === record.group.children[record.group.children.length - 1];
            }

            else {
                
                rowValues.isFirstRow = idx === 0;
                if (!rowValues.isFirstRow) {
                    prev = dataSource.getAt(idx - 1);
                    
                    if (prev) {
                        
                        rowValues.isFirstRow = !prev.isEqual(grouper.getGroupString(prev), groupName);
                    }
                }

                
                rowValues.isLastRow = idx == dataSource.getTotalCount() - 1;
                if (!rowValues.isLastRow) {
                    next = dataSource.getAt(idx + 1);
                    if (next) {
                        
                        rowValues.isLastRow = !next.isEqual(grouper.getGroupString(next), groupName);
                    }
                }
            }

            if (rowValues.isFirstRow) {
                groupInfo.groupField = groupField;
                groupInfo.name = groupName;
                groupInfo.groupValue = record.get(groupField);
                groupInfo.columnName = header ? header.text : groupField;
                rowValues.collapsibleCls = me.collapsible ? me.collapsibleCls : me.hdNotCollapsibleCls;
                rowValues.groupId = me.createGroupId(groupName);

                if (!me.isExpanded(groupName)) {
                    rowValues.itemClasses.push(me.hdCollapsedCls);
                    rowValues.isCollapsedGroup = true;
                }

                
                if (dataSource.buffered) {
                    groupInfo.rows = groupInfo.children = [];
                } else {
                    groupInfo.rows = groupInfo.children = me.getRecordGroup(record).children;
                }
                rowValues.groupInfo = groupInfo;
            }

            if (rowValues.isLastRow) {
                
                if (me.showSummaryRow) {
                    rowValues.summaryRecord = data.summaryData[groupName];
                }
            }
            rowValues.needsWrap = (rowValues.isFirstRow || rowValues.summaryRecord);
        }
    },

    setup: function(rows, rowValues) {
        var me = this,
            data = me.refreshData,
            isGrouping = !me.disabled && me.view.store.isGrouped();
            
        me.skippedRows = 0;
        if (rowValues.view.bufferedRenderer) {
            rowValues.view.bufferedRenderer.variableRowHeight = true;
        }
        data.groupField = me.getGroupField();
        data.header = me.getGroupedHeader(data.groupField);
        data.doGrouping = isGrouping;
        rowValues.groupHeaderTpl = Ext.XTemplate.getTpl(me, 'groupHeaderTpl');

        if (isGrouping && me.showSummaryRow) {
            data.summaryData = me.generateSummaryData();
        }
    },

    cleanup: function(rows, rowValues) {
        var data = this.refreshData;

        rowValues.groupInfo = rowValues.groupHeaderTpl = rowValues.isFirstRow = null;
        data.groupField = data.header = null;
    },

    getGroupName: function(element) {
        var me = this,
            view = me.view,
            eventSelector = me.eventSelector,
            parts,
            targetEl,
            row;

        
        targetEl = Ext.fly(element).findParent(eventSelector);

        if (!targetEl) {
            
            row = Ext.fly(element).findParent(view.itemSelector);
            if (row) {
                targetEl = row.down(eventSelector, true);
            }
        }

        if (targetEl) {
            parts = targetEl.id.split(view.id + '-hd-');
            if (parts.length === 2) {
                return Ext.htmlDecode(parts[1]);
            }
        }
    },

    
    getRecordGroup: function(record) {
        var grouper = this.view.store.groupers.first();
        if (grouper) {
            return this.groupCache[grouper.getGroupString(record)];
        }
    },

    createGroupId: function(group) {
        return this.view.id + '-hd-' + Ext.htmlEncode(group);
    },

    createGroupCls: function(group) {
        return this.view.id + '-' + Ext.htmlEncode(group) + '-item';    
    },

    getGroupField: function(){
        return this.view.store.getGroupField();
    },

    getGroupedHeader: function(groupField) {
        var me = this,
            headerCt = me.view.headerCt,
            partner = me.lockingPartner,
            selector, header;

        groupField = groupField || this.getGroupField();

        if (groupField) {
            selector = '[dataIndex=' + groupField + ']';
            header = headerCt.down(selector);
            
            if (!header && partner) {
                header = partner.view.headerCt.down(selector);
            }
        }
        return header || null;
    },

    getFireEventArgs: function(type, view, targetEl, e) {
        return [type, view, targetEl, this.getGroupName(targetEl), e];
    },

    destroy: function(){
        var me = this,
            dataSource = me.dataSource;

        me.view = me.prunedHeader = me.grid = me.groupCache = me.dataSource = null;
        me.callParent();
        if (dataSource) {
            dataSource.bindStore(null);
        }
    },

    onReconfigure: function(grid, store, columns, oldStore, oldColumns) {
        var me = grid;

        if (store && store !== oldStore) {
            
            if (store.buffered !== oldStore.buffered) {
                Ext.Error.raise('Cannot reconfigure grouping switching between buffered and non-buffered stores');
            }
            if (store.buffered) {
                me.bindStore(store);
                me.dataSource.processStore(store);
            }
        }
    }
});


Ext.define('Ext.grid.feature.GroupingSummary', {

    extend:  Ext.grid.feature.Grouping ,

    alias: 'feature.groupingsummary',

    showSummaryRow: true,
    
    vetoEvent: function(record, row, rowIndex, e){
        var result = this.callParent(arguments);
        if (result !== false) {
            if (e.getTarget(this.summaryRowSelector)) {
                result = false;
            }
        }
        return result;
    }
});


Ext.define('Ext.grid.feature.RowBody', {
    extend:  Ext.grid.feature.Feature ,
    alias: 'feature.rowbody',

    rowBodyCls: Ext.baseCSSPrefix + 'grid-row-body',
    rowBodyHiddenCls: Ext.baseCSSPrefix + 'grid-row-body-hidden',
    rowBodyTdSelector: 'td.' + Ext.baseCSSPrefix + 'grid-cell-rowbody',
    eventPrefix: 'rowbody',
    eventSelector: 'tr.' + Ext.baseCSSPrefix + 'grid-rowbody-tr',

    colSpanDecrement: 0,

    tableTpl: {
        before: function(values, out) {
            var view = values.view,
                rowValues = view.rowValues;

            this.rowBody.setup(values.rows, rowValues);
        },
        after: function(values, out) {
            var view = values.view,
                rowValues = view.rowValues;

            this.rowBody.cleanup(values.rows, rowValues);
        },
        priority: 100
    },

    extraRowTpl: [
        '{%',
            'values.view.rowBodyFeature.setupRowData(values.record, values.recordIndex, values);',
            'this.nextTpl.applyOut(values, out, parent);',
        '%}',
        '<tr class="', Ext.baseCSSPrefix, 'grid-rowbody-tr {rowBodyCls}" {ariaRowAttr}>',
            '<td class="', Ext.baseCSSPrefix, 'grid-cell-rowbody', '" colspan="{rowBodyColspan}" {ariaCellAttr}>',
                '<div class="', Ext.baseCSSPrefix, 'grid-rowbody', ' {rowBodyDivCls}" {ariaCellInnerAttr}>{rowBody}</div>',
            '</td>',
        '</tr>', {
            priority: 100,

            syncRowHeights: function(firstRow, secondRow) {
                var owner = this.owner,
                    firstRowBody = Ext.fly(firstRow).down(owner.eventSelector, true),
                    secondRowBody,
                    firstHeight, secondHeight;

                
                if (firstRowBody && (secondRowBody = Ext.fly(secondRow).down(owner.eventSelector, true))) {
                    if ((firstHeight = firstRowBody.offsetHeight) > (secondHeight = secondRowBody.offsetHeight)) {
                        Ext.fly(secondRowBody).setHeight(firstHeight);
                    }
                    else if (secondHeight > firstHeight) {
                        Ext.fly(firstRowBody).setHeight(secondHeight);
                    }
                }
            },

            syncContent: function(destRow, sourceRow) {
                var owner = this.owner,
                    destRowBody = Ext.fly(destRow).down(owner.eventSelector, true),
                    sourceRowBody;

                
                if (destRowBody && (sourceRowBody = Ext.fly(sourceRow).down(owner.eventSelector, true))) {
                    Ext.fly(destRowBody).syncContent(sourceRowBody);
                }
            }
        }
    ],

    init: function(grid) {
        var me = this,
            view = me.view;

        view.rowBodyFeature = me;

        
        
        if (!view.findFeature('rowwrap')) {
            grid.mon(view, {
                element: 'el',
                mousedown: me.onMouseDown,
                scope: me
            });
            
            me.mon(grid.getStore(), 'remove', me.onStoreRemove, me);
        }

        view.headerCt.on({
            columnschanged: me.onColumnsChanged,
            scope: me
        });
        view.addTableTpl(me.tableTpl).rowBody = me;
        view.addRowTpl(Ext.XTemplate.getTpl(this, 'extraRowTpl'));
        me.callParent(arguments);
    },
    
    onStoreRemove: function(store, model, index){
        var view = this.view,
            node;
            
        if (view.rendered) {
            node = view.getNode(index);
            if (node) {
                node = Ext.fly(node).next(this.eventSelector);
                if (node) {
                    node.remove();
                }
            }
        }
    },

    
    onMouseDown: function(e) {
        var me = this,
            tableRow = e.getTarget(me.eventSelector);

        
        if (tableRow && Ext.fly(tableRow = tableRow.previousSibling).is(me.view.getItemSelector())) {
            e.target = tableRow;
            me.view.handleEvent(e);
        }
    },

    getSelectedRow: function(view, rowIndex) {
        var selectedRow = view.getNode(rowIndex, false);
        if (selectedRow) {
            return Ext.fly(selectedRow).down(this.eventSelector);
        }
        return null;
    },

    
    onColumnsChanged: function(headerCt) {
        var items = this.view.el.query(this.rowBodyTdSelector),
            colspan = headerCt.getVisibleGridColumns().length,
            len = items.length,
            i;

        for (i = 0; i < len; ++i) {
            items[i].colSpan = colspan;
        }
    },
    
    
    setupRowData: function(record, rowIndex, rowValues) {
        if (this.getAdditionalData) {
            Ext.apply(rowValues, this.getAdditionalData(record.data, rowIndex, record, rowValues));
        }
    },

    setup: function(rows, rowValues) {
        rowValues.rowBodyCls = this.rowBodyCls;
        rowValues.rowBodyColspan = rowValues.view.getGridColumns().length - this.colSpanDecrement;
    },

    cleanup: function(rows, rowValues) {
        rowValues.rowBodyCls = rowValues.rowBodyColspan = rowValues.rowBody = null;    
    }
});


Ext.define('Ext.grid.feature.RowWrap', {
    extend:  Ext.grid.feature.Feature ,
    alias: 'feature.rowwrap',
    
    rowWrapTd: 'td.' + Ext.baseCSSPrefix + 'grid-rowwrap',
    
    
    hasFeatureEvent: false,
    
    tableTpl: {
        before: function(values, out) {
            if (values.view.bufferedRenderer) {
                values.view.bufferedRenderer.variableRowHeight = true;
            }
        },
        priority: 200
    },

    wrapTpl: [
        '<tr data-boundView="{view.id}" data-recordId="{record.internalId}" data-recordIndex="{recordIndex}" class="{[values.itemClasses.join(" ")]} ', Ext.baseCSSPrefix, 'grid-wrap-row" {ariaRowAttr}>',
            '<td class="', Ext.baseCSSPrefix, 'grid-rowwrap ', Ext.baseCSSPrefix, 'grid-td" colSpan="{columns.length}" {ariaCellAttr}>',
                '<table class="', Ext.baseCSSPrefix, '{view.id}-table ', Ext.baseCSSPrefix, 'grid-table" border="0" cellspacing="0" cellpadding="0" style="width:100%" {ariaCellInnerTableAttr}>',
                    '{[values.view.renderColumnSizer(out)]}',
                    '{%',
                        'values.itemClasses.length = 0;',
                        'this.nextTpl.applyOut(values, out, parent)',
                    '%}',
                '</table>',
            '</td>',
        '</tr>', {
            priority: 200
        }
    ],

    init: function(grid) {
        var me = this;
        me.view.addTableTpl(me.tableTpl);
        me.view.addRowTpl(Ext.XTemplate.getTpl(me, 'wrapTpl'));
        me.view.headerCt.on({
            columnhide: me.onColumnHideShow,
            columnshow: me.onColumnHideShow,
            scope: me
        });
    },

    
    onColumnHideShow: function() {
        var view = this.view,
            items = view.el.query(this.rowWrapTd),
            colspan = view.headerCt.getVisibleGridColumns().length,
            len = items.length,
            i;
            
        for (i = 0; i < len; ++i) {
            items[i].colSpan = colspan;
        }
    }
});


Ext.define('Ext.grid.feature.Summary', {

    

    extend:  Ext.grid.feature.AbstractSummary ,

    alias: 'feature.summary',

    
    dock: undefined,

    dockedSummaryCls: Ext.baseCSSPrefix + 'docked-summary',

    panelBodyCls: Ext.baseCSSPrefix + 'summary-',
    
    scrollPadProperty: 'padding-right',

    init: function(grid) {
        var me = this,
            view = me.view;

        me.callParent(arguments);

        if (me.dock) {
            grid.headerCt.on({
                afterlayout: me.onStoreUpdate,
                scope: me
            });
            grid.on({
                beforerender: function() {
                    var tableCls = [me.summaryTableCls];
                    if (view.columnLines) {
                        tableCls[tableCls.length] = view.ownerCt.colLinesCls;
                    }
                    me.summaryBar = grid.addDocked({
                        childEls: ['innerCt'],
                        renderTpl: [
                            '<div id="{id}-innerCt" role="presentation">',
                                '<table cellPadding="0" cellSpacing="0" class="' + tableCls.join(' ') + '">',
                                    '<tr class="' + me.summaryRowCls + '"></tr>',
                                '</table>',
                            '</div>'
                        ],
                        style: 'overflow:hidden',
                        itemId: 'summaryBar',
                        cls: [ me.dockedSummaryCls, me.dockedSummaryCls + '-' + me.dock ],
                        xtype: 'component',
                        dock: me.dock,
                        weight: 10000000
                    })[0];
                },
                afterrender: function() {
                    grid.body.addCls(me.panelBodyCls + me.dock);
                    view.mon(view.el, {
                        scroll: me.onViewScroll,
                        scope: me
                    });
                    me.onStoreUpdate();
                },
                single: true
            });

            
            grid.headerCt.afterComponentLayout = Ext.Function.createSequence(grid.headerCt.afterComponentLayout, function() {
                var width = this.getFullWidth(),
                    innerCt = me.summaryBar.innerCt,
                    scrollWidth;
                    
                if (view.hasVerticalScroll()) {
                    scrollWidth = Ext.getScrollbarSize().width;
                    width -= scrollWidth;
                    innerCt.down('table').setStyle(me.scrollPadProperty, scrollWidth + 'px');
                }
                innerCt.setWidth(width);
            });
        } else {
            me.view.addFooterFn(me.renderTFoot);
        }

        grid.on({
            columnmove: me.onStoreUpdate,
            scope: me
        });

        
        view.mon(view.store, {
            update: me.onStoreUpdate,
            datachanged: me.onStoreUpdate,
            scope: me
        });
    },

    renderTFoot: function(values, out) {
        var view = values.view,
            me = view.findFeature('summary');

        if (me.showSummaryRow) {
            out.push('<tfoot>');
            me.outputSummaryRecord(me.createSummaryRecord(view), values, out);
            out.push('</tfoot>');
        }
    },
    
    toggleSummaryRow: function(visible) {
        var me = this,
            bar = me.summaryBar;
            
        me.callParent(arguments);
        if (bar) {
            bar.setVisible(me.showSummaryRow);
            me.onViewScroll();
        }
    },
    
    vetoEvent: function(record, row, rowIndex, e) {
        return !e.getTarget(this.summaryRowSelector);
    },

    onViewScroll: function() {
        this.summaryBar.el.dom.scrollLeft = this.view.el.dom.scrollLeft;
    },

    createSummaryRecord: function(view) {
        var columns = view.headerCt.getVisibleGridColumns(),
            info = {
                records: view.store.getRange()
            },
            colCount = columns.length, i, column,
            summaryRecord = this.summaryRecord || (this.summaryRecord = new view.store.model(null, view.id + '-summary-record'));

        
        summaryRecord.beginEdit();
        for (i = 0; i < colCount; i++) {
            column = columns[i];

            
            
            if (!column.dataIndex) {
                column.dataIndex = column.id;
            }

            summaryRecord.set(column.dataIndex, this.getSummary(view.store, column.summaryType, column.dataIndex, info));
        }
        summaryRecord.endEdit(true);
        
        summaryRecord.commit(true);
        summaryRecord.isSummary = true;

        return summaryRecord;
    },

    onStoreUpdate: function() {
        var me = this,
            view = me.view,
            record = me.createSummaryRecord(view),
            newRowDom = view.createRowElement(record, -1),
            oldRowDom, partner,
            p;

        if (!view.rendered) {
            return;
        }
        
        
        if (me.dock) {
            oldRowDom = me.summaryBar.el.down('.' + me.summaryRowCls, true);
        }
        
        
        else {
            oldRowDom = me.view.getNode(record);
        }
        
        if (oldRowDom) {
            p = oldRowDom.parentNode;
            p.insertBefore(newRowDom, oldRowDom);
            p.removeChild(oldRowDom);

            partner = me.lockingPartner;
            
            
            if (partner && partner.grid.rendered && !me.calledFromLockingPartner) {
                partner.calledFromLockingPartner = true;
                partner.onStoreUpdate();
                partner.calledFromLockingPartner = false;
            }
        }
        
        if (me.dock) {
            me.onColumnHeaderLayout();
        }
    },

    
    onColumnHeaderLayout: function() {
        var view = this.view,
            columns = view.headerCt.getVisibleGridColumns(),
            column,
            len = columns.length, i,
            summaryEl = this.summaryBar.el,
            el;

        for (i = 0; i < len; i++) {
            column = columns[i];
            el = summaryEl.down(view.getCellSelector(column));
            if (el) {
                if (column.hidden) {
                    el.setDisplayed(false);
                } else {
                    el.setDisplayed(true);
                    el.setWidth(column.width || (column.lastBox ? column.lastBox.width : 100));
                }
            }
        }
    }
});


Ext.define('Ext.grid.locking.HeaderContainer', {
    extend:  Ext.grid.header.Container ,
               
                                
      

    constructor: function(lockable) {
        var me = this,
            events,
            event,
            eventNames = [],
            lockedGrid = lockable.lockedGrid,
            normalGrid = lockable.normalGrid;

        me.lockable = lockable;
        me.callParent();

        
        lockedGrid.visibleColumnManager.rootColumns =
            normalGrid.visibleColumnManager.rootColumns =
            lockable.visibleColumnManager =
            me.visibleColumnManager = new Ext.grid.ColumnManager(true, lockedGrid.headerCt, normalGrid.headerCt);
            
        lockedGrid.columnManager.rootColumns =
            normalGrid.columnManager.rootColumns =
            lockable.columnManager =
            me.columnManager = new Ext.grid.ColumnManager(false, lockedGrid.headerCt, normalGrid.headerCt);

        
        events = lockedGrid.headerCt.events;
        for (event in events) {
            if (events.hasOwnProperty(event)) {
                eventNames.push(event);
            }
        }
        me.relayEvents(lockedGrid.headerCt, eventNames);
        me.relayEvents(normalGrid.headerCt, eventNames);
    },

    getRefItems: function() {
        return this.lockable.lockedGrid.headerCt.getRefItems().concat(this.lockable.normalGrid.headerCt.getRefItems());
    },

    
    
    getGridColumns: function() {
        return this.lockable.lockedGrid.headerCt.getGridColumns().concat(this.lockable.normalGrid.headerCt.getGridColumns());
    },

    
    getColumnsState: function () {
        var me = this,
            locked = me.lockable.lockedGrid.headerCt.getColumnsState(),
            normal = me.lockable.normalGrid.headerCt.getColumnsState();

        return locked.concat(normal);
    },

    
    applyColumnsState: function (columns) {
        var me             = this,
            lockedGrid     = me.lockable.lockedGrid,
            lockedHeaderCt = lockedGrid.headerCt,
            normalHeaderCt = me.lockable.normalGrid.headerCt,
            lockedCols     = Ext.Array.toValueMap(lockedHeaderCt.items.items, 'headerId'),
            normalCols     = Ext.Array.toValueMap(normalHeaderCt.items.items, 'headerId'),
            locked         = [],
            normal         = [],
            lockedWidth    = 1,
            length         = columns.length,
            i, existing,
            lockedDefault,
            col;

        for (i = 0; i < length; i++) {
            col = columns[i];

            lockedDefault = lockedCols[col.id];
            existing = lockedDefault || normalCols[col.id];

            if (existing) {
                if (existing.applyColumnState) {
                    existing.applyColumnState(col);
                }
                if (existing.locked === undefined) {
                    existing.locked = !!lockedDefault;
                }
                if (existing.locked) {
                    locked.push(existing);
                    if (!existing.hidden && typeof existing.width == 'number') {
                        lockedWidth += existing.width;
                    }
                } else {
                    normal.push(existing);
                }
            }
        }

        
        if (locked.length + normal.length == lockedHeaderCt.items.getCount() + normalHeaderCt.items.getCount()) {
            lockedHeaderCt.removeAll(false);
            normalHeaderCt.removeAll(false);

            lockedHeaderCt.add(locked);
            normalHeaderCt.add(normal);

            lockedGrid.setWidth(lockedWidth);
        }
    }
});


Ext.define('Ext.grid.locking.View', {
    alternateClassName: 'Ext.grid.LockingView',

    mixins: {
        observable:  Ext.util.Observable 
    },

    
    isLockingView: true,

    eventRelayRe: /^(beforeitem|beforecontainer|item|container|cell|refresh)/,

    constructor: function(config){
        var me = this,
            eventNames = [],
            eventRe = me.eventRelayRe,
            locked = config.locked.getView(),
            normal = config.normal.getView(),
            events,
            event;

        Ext.apply(me, {
            lockedView: locked,
            normalView: normal,
            lockedGrid: config.locked,
            normalGrid: config.normal,
            panel: config.panel
        });
        me.mixins.observable.constructor.call(me, config);

        
        events = locked.events;
        for (event in events) {
            if (events.hasOwnProperty(event) && eventRe.test(event)) {
                eventNames.push(event);
            }
        }
        me.relayEvents(locked, eventNames);
        me.relayEvents(normal, eventNames);

        normal.on({
            scope: me,
            itemmouseleave: me.onItemMouseLeave,
            itemmouseenter: me.onItemMouseEnter
        });

        locked.on({
            scope: me,
            itemmouseleave: me.onItemMouseLeave,
            itemmouseenter: me.onItemMouseEnter
        });
        
        me.panel.on({
            render: me.onPanelRender,
            scope: me
        });
    },

    onPanelRender: function() {
        var me = this,
            mask = me.loadMask,
            cfg = {
                target: me.panel,
                msg: me.loadingText,
                msgCls: me.loadingCls,
                useMsg: me.loadingUseMsg,
                store: me.panel.store
            };

        
        
        me.el = me.panel.body;
        me.fireEvent('render', me);

        if (mask) {
            
            if (Ext.isObject(mask)) {
                cfg = Ext.apply(cfg, mask);
            }
            
            
            
            
            me.loadMask = new Ext.LoadMask(cfg);
        }
    },

    getGridColumns: function() {
        var cols = this.lockedGrid.headerCt.getVisibleGridColumns();
        return cols.concat(this.normalGrid.headerCt.getVisibleGridColumns());
    },

    getEl: function(column){
        return this.getViewForColumn(column).getEl();
    },

    getViewForColumn: function(column) {
        var view = this.lockedView,
            inLocked;

        view.headerCt.cascade(function(col){
            if (col === column) {
                inLocked = true;
                return false;
            }
        });

        return inLocked ? view : this.normalView;
    },

    onItemMouseEnter: function(view, record){
        var me = this,
            locked = me.lockedView,
            other = me.normalView,
            item;

        if (view.trackOver) {
            if (view !== locked) {
                other = locked;
            }
            item = other.getNode(record, false);
            other.highlightItem(item);
        }
    },

    onItemMouseLeave: function(view, record){
        var me = this,
            locked = me.lockedView,
            other = me.normalView;

        if (view.trackOver) {
            if (view !== locked) {
                other = locked;
            }
            other.clearHighlight();
        }
    },

    relayFn: function(name, args){
        args = args || [];

        var view = this.lockedView;
        view[name].apply(view, args);
        view = this.normalView;
        view[name].apply(view, args);
    },

    getSelectionModel: function(){
        return this.panel.getSelectionModel();
    },

    getStore: function(){
        return this.panel.store;
    },

    getNode: function(nodeInfo, dataRow) {
        
        return this.normalView.getNode(nodeInfo, dataRow);
    },

    getCell: function(record, column) {
        var view = this.getViewForColumn(column),
            row = view.getNode(record, true);
            
        return Ext.fly(row).down(column.getCellSelector());
    },

    indexOf: function(record) {
        var result = this.lockedView.indexOf(record);
        if (!result) {
            result = this.normalView.indexOf(record);
        }
        return result;
    },

    focus: function() {
        var p = this.getSelectionModel().getCurrentPosition(),
            v = p ? p.view : this.normalView;

        v.focus();
    },
    
    focusRow: function(row) {
        this.normalView.focusRow(row);
    },

    focusCell: function(position) {
        position.view.focusCell(position);
    },

    isVisible: function(deep) {
        return this.panel.isVisible(deep);
    },
    
    getCellByPosition: function(pos, returnDom) {
        var col = pos.column,
            lockedSize = this.lockedGrid.getColumnManager().getColumns().length;
            
        
        if (col >= lockedSize) {
            
            pos = Ext.apply({}, pos);
            pos.column -= lockedSize;
            return this.normalView.getCellByPosition(pos, returnDom);
        } else {
            return this.lockedView.getCellByPosition(pos, returnDom);
        }
    },

    getRecord: function(node) {
        var result = this.lockedView.getRecord(node);
        if (!result) {
            result = this.normalView.getRecord(node);
        }
        return result;
    },
    
    scrollBy: function(){
        var normal = this.normalView;
        normal.scrollBy.apply(normal, arguments);
    },

    addElListener: function(eventName, fn, scope){
        this.relayFn('addElListener', arguments);
    },

    refreshNode: function(){
        this.relayFn('refreshNode', arguments);
    },

    refresh: function(){
        this.relayFn('refresh', arguments);
    },

    bindStore: function(){
        this.relayFn('bindStore', arguments);
    },

    addRowCls: function(){
        this.relayFn('addRowCls', arguments);
    },

    removeRowCls: function(){
        this.relayFn('removeRowCls', arguments);
    },
    
    destroy: function(){
        var me = this,
            mask = me.loadMask;
            
        
        
        me.clearListeners();
        if (mask && mask.bindStore) {
            mask.bindStore(null);
        }
    }

});


Ext.define('Ext.grid.locking.Lockable', {
    alternateClassName: 'Ext.grid.Lockable',

               
                                
                                    
                                           
                        
      

    
    syncRowHeight: true,

    

    

    

    headerCounter: 0,

    
    scrollDelta: 40,
    
    
    
    
    
    lockedGridCls: Ext.baseCSSPrefix + 'grid-inner-locked',

    
    
    unlockText: 'Unlock',
    
    
    lockText: 'Lock',
    

    
    
    bothCfgCopy: [
        'invalidateScrollerOnRefresh',
        'hideHeaders',
        'enableColumnHide',
        'enableColumnMove',
        'enableColumnResize',
        'sortableColumns',
        'columnLines',
        'rowLines'
    ],
    normalCfgCopy: [ 
        'verticalScroller', 
        'verticalScrollDock', 
        'verticalScrollerType', 
        'scroll'
    ],
    lockedCfgCopy: [],

    determineXTypeToCreate: function(lockedSide) {
        var me = this,
            typeToCreate,
            xtypes, xtypesLn, xtype, superxtype;

        if (me.subGridXType) {
            typeToCreate = me.subGridXType;
        } else {
            
            
            if (!lockedSide) {
                return 'gridpanel';
            }
            xtypes     = this.getXTypes().split('/');
            xtypesLn   = xtypes.length;
            xtype      = xtypes[xtypesLn - 1];
            superxtype = xtypes[xtypesLn - 2];

            if (superxtype !== 'tablepanel') {
                typeToCreate = superxtype;
            } else {
                typeToCreate = xtype;
            }
        }

        return typeToCreate;
    },

    
    
    injectLockable: function() {
        
        this.lockable = true;
        
        
        this.hasView = true;

        var me = this,
            scrollbarHeight = Ext.getScrollbarSize().height,
            store = me.store = Ext.StoreManager.lookup(me.store),

            
            selModel = me.getSelectionModel(),

            
            allFeatures,

            
            allPlugins,

            lockedGrid,
            normalGrid,
            i,
            columns,
            lockedHeaderCt,
            normalHeaderCt,
            lockedView,
            normalView,
            listeners,
            loadMask = me.viewConfig && me.viewConfig.loadMask,
            bufferedRenderer = me.findPlugin('bufferedrenderer');

        allFeatures = me.constructLockableFeatures();

        
        if (me.features) {
            me.features = null;
        }

        
        allPlugins = me.constructLockablePlugins();
        me.plugins = allPlugins.topPlugins;

        lockedGrid = Ext.apply({
            id: me.id + '-locked',
            isLocked: true,
            ownerLockable: me,
            xtype: me.determineXTypeToCreate(true),
            store: store,
            scrollerOwner: false,
            
            
            animate: false,
            
            scroll: scrollbarHeight ? false : 'vertical',
            selModel: selModel,
            border: false,
            cls: me.lockedGridCls,
            isLayoutRoot: function() {
                return false;
            },
            features: allFeatures.lockedFeatures,
            plugins: allPlugins.lockedPlugins
        }, me.lockedGridConfig);

        normalGrid = Ext.apply({
            id: me.id + '-normal',
            isLocked: false,
            ownerLockable: me,
            xtype: me.determineXTypeToCreate(),
            store: store,
            scrollerOwner: false,
            selModel: selModel,
            border: false,
            isLayoutRoot: function() {
                return false;
            },
            features: allFeatures.normalFeatures,
            plugins: allPlugins.normalPlugins
        }, me.normalGridConfig);

        me.addCls(Ext.baseCSSPrefix + 'grid-locked');

        
        
        
        Ext.copyTo(normalGrid, me, me.bothCfgCopy, true);
        Ext.copyTo(lockedGrid, me, me.bothCfgCopy, true);
        Ext.copyTo(normalGrid, me, me.normalCfgCopy, true);
        Ext.copyTo(lockedGrid, me, me.lockedCfgCopy, true);
        for (i = 0; i < me.normalCfgCopy.length; i++) {
            delete me[me.normalCfgCopy[i]];
        }
        for (i = 0; i < me.lockedCfgCopy.length; i++) {
            delete me[me.lockedCfgCopy[i]];
        }

        me.addEvents(
            
            'processcolumns',

            
            'lockcolumn',

            
            'unlockcolumn'
        );

        me.addStateEvents(['lockcolumn', 'unlockcolumn']);

        columns = me.processColumns(me.columns);

        
        
        lockedGrid.width = columns.lockedWidth + Ext.num(selModel.headerWidth, 0) + (columns.locked.items.length ? 1 : 0);
        lockedGrid.columns = columns.locked;
        normalGrid.columns = columns.normal;

        
        normalGrid.flex = 1;
        lockedGrid.viewConfig = me.lockedViewConfig || {};
        lockedGrid.viewConfig.loadingUseMsg = false;
        lockedGrid.viewConfig.loadMask = false;

        
        
        
        
        if (scrollbarHeight) {
            lockedGrid.viewConfig.style = 'border-bottom:' + scrollbarHeight +
                'px solid #f6f6f6;' + (lockedGrid.viewConfig.style || '');
        }

        normalGrid.viewConfig = me.normalViewConfig || {};
        normalGrid.viewConfig.loadMask = false;


        Ext.applyIf(lockedGrid.viewConfig, me.viewConfig);
        Ext.applyIf(normalGrid.viewConfig, me.viewConfig);

        me.lockedGrid = Ext.ComponentManager.create(lockedGrid);

        if (me.isTree) {
            
            me.lockedGrid.getView().animate = false;

            
            normalGrid.store = me.lockedGrid.view.store;

            
            normalGrid.deferRowRender = false;

            
            normalGrid.viewConfig.stripeRows = me.lockedGrid.view.stripeRows;
            normalGrid.rowLines = me.lockedGrid.rowLines;
        }

        
        lockedView = me.lockedGrid.getView();
        normalGrid.viewConfig.lockingPartner = lockedView;
        me.normalGrid = Ext.ComponentManager.create(normalGrid);
        lockedView.lockingPartner = normalView = me.normalGrid.getView();

        me.view = new Ext.grid.locking.View({
            loadingText: normalView.loadingText,
            loadingCls: normalView.loadingCls,
            loadingUseMsg: normalView.loadingUseMsg,
            
            loadMask: loadMask === undefined ? me.loadMask : loadMask,
            locked: me.lockedGrid,
            normal: me.normalGrid,
            panel: me
        });

        
        listeners = bufferedRenderer ? {} : {
            scroll: {
                fn: me.onLockedViewScroll,
                element: 'el',
                scope: me
            }
        };

        
        
        
        
        if (scrollbarHeight) {
            me.lockedGrid.on({
                afterlayout: me.afterLockedViewLayout,
                scope: me
            });

            
            lockedView.getOverflowStyle();

            
            if (lockedView.scrollFlags.y) {
                me.lockedGrid.headerCt.forceFit = true;
            }
            
            else {
                listeners.mousewheel = {
                    fn: me.onLockedViewMouseWheel,
                    element: 'el',
                    scope: me
                };
            }
        }
        lockedView.on(listeners);

        
        listeners = bufferedRenderer ? {} : {
            scroll: {
                fn: me.onNormalViewScroll,
                element: 'el',
                scope: me
            },
            scope: me
        };
        normalView.on(listeners);

        lockedHeaderCt = me.lockedGrid.headerCt;
        normalHeaderCt = me.normalGrid.headerCt;

        
        
        me.headerCt = me.view.headerCt = new Ext.grid.locking.HeaderContainer(me);

        lockedHeaderCt.lockedCt = true;
        lockedHeaderCt.lockableInjected = true;
        normalHeaderCt.lockableInjected = true;

        lockedHeaderCt.on({
            
            add: {
                buffer: 1,
                scope: me,
                fn: me.onLockedHeaderAdd
            },
            columnshow: me.onLockedHeaderShow,
            columnhide: me.onLockedHeaderHide,
            sortchange: me.onLockedHeaderSortChange,
            columnresize: me.onLockedHeaderResize,
            scope: me
        });

        normalHeaderCt.on({
            sortchange: me.onNormalHeaderSortChange,
            scope: me
        });

        me.modifyHeaderCt();
        me.items = [me.lockedGrid, me.normalGrid];

        me.relayHeaderCtEvents(lockedHeaderCt);
        me.relayHeaderCtEvents(normalHeaderCt);

        
        
        me.storeRelayers = me.relayEvents(store, [
            
            'filterchange',
            
            'groupchange'
        ]);

        me.layout = {
            type: 'hbox',
            align: 'stretch'
        };
    },

    getLockingViewConfig: function(){
        return {
            xclass: 'Ext.grid.locking.View',
            locked: this.lockedGrid,
            normal: this.normalGrid,
            panel: this
        };
    },

    processColumns: function(columns) {
        
        var i,
            len,
            column,
            cp = new Ext.grid.header.Container(),
            lockedHeaders = [],
            normalHeaders = [],
            lockedHeaderCt = {
                itemId: 'lockedHeaderCt',
                stretchMaxPartner: '^^>>#normalHeaderCt',
                items: lockedHeaders
            },
            normalHeaderCt = {
                itemId: 'normalHeaderCt',
                stretchMaxPartner: '^^>>#lockedHeaderCt',
                items: normalHeaders
            },
            result = {
                lockedWidth: 0,
                locked: lockedHeaderCt,
                normal: normalHeaderCt
            };

        
        if (Ext.isObject(columns)) {
            Ext.applyIf(lockedHeaderCt, columns);
            Ext.applyIf(normalHeaderCt, columns);
            Ext.apply(cp, columns);
            columns = columns.items;
        }

        for (i = 0, len = columns.length; i < len; ++i) {
            column = columns[i];

            
            
            
            if (!column.isComponent) {
                column = cp.lookupComponent(cp.applyDefaults(column));
            }

            
            
            column.processed = true;
            if (column.locked || column.autoLock) {
                if (!column.hidden) {
                    result.lockedWidth += this.getColumnWidth(column) || cp.defaultWidth;
                }
                lockedHeaders.push(column);
            } else {
                normalHeaders.push(column);
            }
            if (!column.headerId) {
                column.headerId = (column.initialConfig || column).id || ('h' + (++this.headerCounter));
            }
        }
        this.fireEvent('processcolumns', this, lockedHeaders, normalHeaders);
        cp.destroy();
        return result;
    },

    
    
    getColumnWidth: function(column) {
        var result = column.width || 0,
            subcols, len, i;

        if (!result && column.isGroupHeader) {
            subcols = column.items.items;
            len = subcols.length;
            for (i = 0; i < len; i++) {
                result += this.getColumnWidth(subcols[i]);
            }
        }
        return result;
    },

    
    
    
    afterLockedViewLayout: function() {
        var me = this,
            lockedView = me.lockedGrid.getView(),
            lockedViewEl = lockedView.el.dom,
            spacerHeight = (me.normalGrid.headerCt.tooNarrow ? Ext.getScrollbarSize().height : 0);

        
        if (lockedView.scrollFlags.x && lockedViewEl.scrollWidth > lockedViewEl.clientWidth) {
            spacerHeight = 0;
        }

        lockedView.el.dom.style.borderBottomWidth = spacerHeight + 'px';

        
        
        if (!Ext.isBorderBox) {
            lockedView.el.setHeight(lockedView.lastBox.height);
        }
    },

    
    onLockedViewMouseWheel: function(e) {
        var me = this,
            scrollDelta = -me.scrollDelta,
            deltaY = scrollDelta * e.getWheelDeltas().y,
            vertScrollerEl = me.lockedGrid.getView().el.dom,
            verticalCanScrollDown, verticalCanScrollUp;

        if (!me.ignoreMousewheel) {
            if (vertScrollerEl) {
                verticalCanScrollDown = vertScrollerEl.scrollTop !== vertScrollerEl.scrollHeight - vertScrollerEl.clientHeight;
                verticalCanScrollUp   = vertScrollerEl.scrollTop !== 0;
            }

            if ((deltaY < 0 && verticalCanScrollUp) || (deltaY > 0 && verticalCanScrollDown)) {
                e.stopEvent();

                
                
                
                vertScrollerEl.scrollTop += deltaY;
                me.normalGrid.getView().el.dom.scrollTop = vertScrollerEl.scrollTop;

                
                me.onNormalViewScroll();
            }
        }
    },

    onLockedViewScroll: function() {
        var me = this,
            lockedView = me.lockedGrid.getView(),
            normalView = me.normalGrid.getView(),
            normalDom = normalView.el.dom,
            lockedDom = lockedView.el.dom,
            normalTable,
            lockedTable;

        
        if (normalDom.scrollTop !== lockedDom.scrollTop) {
            normalDom.scrollTop = lockedDom.scrollTop;
    
            
            if (me.store.buffered) {
                lockedTable = lockedView.el.child('table', true);
                normalTable = normalView.el.child('table', true);
                normalTable.style.position = 'absolute';
                normalTable.style.top = lockedTable.style.top;
            }
        }
    },
    
    onNormalViewScroll: function() {
        var me = this,
            lockedView = me.lockedGrid.getView(),
            normalView = me.normalGrid.getView(),
            normalDom = normalView.el.dom,
            lockedDom = lockedView.el.dom,
            normalTable,
            lockedTable;

        
        
        
        
        
        
        
        
        
        
        if (normalDom.scrollTop !== lockedDom.scrollTop) {
            lockedDom.scrollTop = normalDom.scrollTop;
    
            
            if (me.store.buffered) {
                lockedTable = lockedView.el.child('table', true);
                normalTable = normalView.el.child('table', true);
                lockedTable.style.position = 'absolute';
                lockedTable.style.top = normalTable.style.top;
            }
        }
    },

    
    syncRowHeights: function() {
        var me = this,
            i,
            lockedView = me.lockedGrid.getView(),
            normalView = me.normalGrid.getView(),
            lockedRowEls = lockedView.all.slice(),
            normalRowEls = normalView.all.slice(),
            ln = lockedRowEls.length,
            scrollTop;

        
        if (normalRowEls.length === ln) {

            
            for (i = 0; i < ln; i++) {
                normalView.syncRowHeights(lockedRowEls[i], normalRowEls[i]);
            }

            
            scrollTop = normalView.el.dom.scrollTop;
            normalView.el.dom.scrollTop = scrollTop;
            lockedView.el.dom.scrollTop = scrollTop;
        }
    },

    
    
    modifyHeaderCt: function() {
        var me = this;
        me.lockedGrid.headerCt.getMenuItems = me.getMenuItems(me.lockedGrid.headerCt.getMenuItems, true);
        me.normalGrid.headerCt.getMenuItems = me.getMenuItems(me.normalGrid.headerCt.getMenuItems, false);
        me.lockedGrid.headerCt.showMenuBy = Ext.Function.createInterceptor(me.lockedGrid.headerCt.showMenuBy, me.showMenuBy);
        me.normalGrid.headerCt.showMenuBy = Ext.Function.createInterceptor(me.normalGrid.headerCt.showMenuBy, me.showMenuBy);
    },

    onUnlockMenuClick: function() {
        this.unlock();
    },

    onLockMenuClick: function() {
        this.lock();
    },

    showMenuBy: function(t, header) {
        var menu = this.getMenu(),
            unlockItem  = menu.down('#unlockItem'),
            lockItem = menu.down('#lockItem'),
            sep = unlockItem.prev();

        if (header.lockable === false) {
            sep.hide();
            unlockItem.hide();
            lockItem.hide();
        } else {
            sep.show();
            unlockItem.show();
            lockItem.show();
            if (!unlockItem.initialConfig.disabled) {
                unlockItem.setDisabled(header.lockable === false);
            }
            if (!lockItem.initialConfig.disabled) {
                lockItem.setDisabled(!header.isLockable());
            }
        }
    },

    getMenuItems: function(getMenuItems, locked) {
        var me            = this,
            unlockText    = me.unlockText,
            lockText      = me.lockText,
            unlockCls     = Ext.baseCSSPrefix + 'hmenu-unlock',
            lockCls       = Ext.baseCSSPrefix + 'hmenu-lock',
            unlockHandler = Ext.Function.bind(me.onUnlockMenuClick, me),
            lockHandler   = Ext.Function.bind(me.onLockMenuClick, me);

        
        return function() {

            
            
            var o = getMenuItems.call(this);
            o.push('-', {
                itemId: 'unlockItem',
                iconCls: unlockCls,
                text: unlockText,
                handler: unlockHandler,
                disabled: !locked
            });
            o.push({
                itemId: 'lockItem',
                iconCls: lockCls,
                text: lockText,
                handler: lockHandler,
                disabled: locked
            });
            return o;
        };
    },

    
    syncLockedWidth: function() {
        var me = this,
            locked = me.lockedGrid,
            lockedView = locked.view,
            lockedViewEl = lockedView.el.dom,
            normal = me.normalGrid,
            lockedColCount = locked.headerCt.getVisibleGridColumns().length,
            normalColCount = normal.headerCt.getVisibleGridColumns().length;

        Ext.suspendLayouts();

        
        
        if (normalColCount) {
            normal.show();
            if (lockedColCount) {

                
                
                if (!locked.headerCt.forceFit) {
                    delete locked.flex;
                    
                    locked.setWidth(locked.headerCt.getFullWidth());
                }
                locked.addCls(me.lockedGridCls);
                locked.show();
            } else {
                
                
                
                locked.getView().refresh();
                locked.hide();
            }

            
            lockedView.el.setStyle(lockedView.getOverflowStyle());

            
            me.ignoreMousewheel = lockedView.scrollFlags.y;
        }

        
        
        else {
            normal.hide();
            
            
            lockedViewEl.style.borderBottomWidth = '0';

            
            locked.flex = 1;
            delete locked.width;
            locked.removeCls(me.lockedGridCls);
            locked.show();

            
            
            lockedView.el.setStyle(normal.view.getOverflowStyle());
            me.ignoreMousewheel = true;
        }
        Ext.resumeLayouts(true);
        return [lockedColCount, normalColCount];
    },

    onLockedHeaderAdd: function() {
        
        
        if (!this.ignoreAddLockedColumn) {
            this.syncLockedWidth();
        }
    },

    onLockedHeaderResize: function() {
        this.syncLockedWidth();
    },

    onLockedHeaderHide: function() {
        this.syncLockedWidth();
    },

    onLockedHeaderShow: function() {
        this.syncLockedWidth();
    },

    onLockedHeaderSortChange: function(headerCt, header, sortState) {
        if (sortState) {
            
            
            this.normalGrid.headerCt.clearOtherSortStates(null, true);
        }
    },

    onNormalHeaderSortChange: function(headerCt, header, sortState) {
        if (sortState) {
            
            
            this.lockedGrid.headerCt.clearOtherSortStates(null, true);
        }
    },

    
    
    lock: function(activeHd, toIdx, toCt) {
        var me         = this,
            normalGrid = me.normalGrid,
            lockedGrid = me.lockedGrid,
            normalHCt  = normalGrid.headerCt,
            refreshFlags,
            ownerCt;

        activeHd = activeHd || normalHCt.getMenu().activeHeader;
        toCt = toCt || lockedGrid.headerCt,
        ownerCt = activeHd.ownerCt;

        
        if (!activeHd.isLockable()) {
            return;
        }

        
        
        if (activeHd.flex) {
            activeHd.width = activeHd.getWidth();
            activeHd.flex = null;
        }

        Ext.suspendLayouts();
        ownerCt.remove(activeHd, false);
        activeHd.locked = true;

        
        me.ignoreAddLockedColumn = true;
        if (Ext.isDefined(toIdx)) {
            toCt.insert(toIdx, activeHd);
        } else {
            toCt.add(activeHd);
        }
        me.ignoreAddLockedColumn = false;

        refreshFlags = me.syncLockedWidth();
        if (refreshFlags[0]) {
            lockedGrid.getView().refresh();
        }
        if (refreshFlags[1]) {
            normalGrid.getView().refresh();
        }
        Ext.resumeLayouts(true);

        me.fireEvent('lockcolumn', me, activeHd);
    },

    
    
    unlock: function(activeHd, toIdx, toCt) {
        var me         = this,
            normalGrid = me.normalGrid,
            lockedGrid = me.lockedGrid,
            lockedHCt  = lockedGrid.headerCt,
            refreshFlags;

        
        if (!Ext.isDefined(toIdx)) {
            toIdx = 0;
        }
        activeHd = activeHd || lockedHCt.getMenu().activeHeader;
        toCt = toCt || normalGrid.headerCt;

        Ext.suspendLayouts();
        activeHd.ownerCt.remove(activeHd, false);
        activeHd.locked = false;
        toCt.insert(toIdx, activeHd);

        
        
        refreshFlags = me.syncLockedWidth();

        if (refreshFlags[0]) {
            lockedGrid.getView().refresh();
        }
        if (refreshFlags[1]) {
            normalGrid.getView().refresh();
        }
        Ext.resumeLayouts(true);

        me.fireEvent('unlockcolumn', me, activeHd);
    },

    
    reconfigureLockable: function(store, columns) {
        var me = this,
            oldStore = me.store,
            lockedGrid = me.lockedGrid,
            normalGrid = me.normalGrid;

        Ext.suspendLayouts();
        if (columns) {
            lockedGrid.headerCt.removeAll();
            normalGrid.headerCt.removeAll();

            columns = me.processColumns(columns);

            
            me.ignoreAddLockedColumn = true;
            lockedGrid.headerCt.add(columns.locked.items);
            me.ignoreAddLockedColumn = false;
            normalGrid.headerCt.add(columns.normal.items);

            
            
            me.syncLockedWidth();
        }

        if (store && store !== oldStore) {
            store = Ext.data.StoreManager.lookup(store);
            me.store = store;
            lockedGrid.bindStore(store);
            normalGrid.bindStore(store);
        } else {
            lockedGrid.getView().refresh();
            normalGrid.getView().refresh();
        }
        Ext.resumeLayouts(true);
    },

    constructLockableFeatures: function() {
        var features = this.features,
            feature,
            featureClone,
            lockedFeatures,
            normalFeatures,
            i = 0, len;

        if (features) {
            if (!Ext.isArray(features)) {
                features = [ features ];
            }
            lockedFeatures = [];
            normalFeatures = [];
            len = features.length;
            for (; i < len; i++) {
                feature = features[i];
                if (!feature.isFeature) {
                    feature = Ext.create('feature.' + feature.ftype, feature);
                }
                switch (feature.lockableScope) {
                    case 'locked':
                        lockedFeatures.push(feature);
                        break;
                    case 'normal':
                        normalFeatures.push(feature);
                        break;
                    default:
                        feature.lockableScope = 'both';
                        lockedFeatures.push(feature);
                        normalFeatures.push(featureClone = feature.clone());

                        
                        featureClone.lockingPartner = feature;
                        feature.lockingPartner = featureClone;
                }
            }
        }
        return {
            normalFeatures: normalFeatures,
            lockedFeatures: lockedFeatures
        };
    },

    constructLockablePlugins: function() {
        var plugins = this.plugins,
            plugin,
            normalPlugin,
            lockedPlugin,
            topPlugins,
            lockedPlugins,
            normalPlugins,
            i = 0, len,
            lockableScope,
            pluginCls;

        if (plugins) {
            if (!Ext.isArray(plugins)) {
                plugins = [ plugins ];
            }
            topPlugins = [];
            lockedPlugins = [];
            normalPlugins = [];
            len = plugins.length;
            for (; i < len; i++) {

                plugin = plugins[i];

                
                if (plugin.init) {
                    lockableScope = plugin.lockableScope;
                }
                
                
                else {
                    pluginCls = plugin.ptype ? Ext.ClassManager.getByAlias(('plugin.' + plugin.ptype)) : Ext.ClassManager.get(plugin.xclass);
                    lockableScope = pluginCls.prototype.lockableScope;
                }

                switch (lockableScope) {
                    case 'both':
                        lockedPlugins.push(lockedPlugin = plugin.clonePlugin());
                        normalPlugins.push(normalPlugin = plugin.clonePlugin());

                        
                        lockedPlugin.lockingPartner = normalPlugin;
                        normalPlugin.lockingPartner = lockedPlugin;
                        
                        
                        
                        Ext.destroy(plugin);
                        break;
                    case 'locked':
                        lockedPlugins.push(plugin);
                        break;
                    case 'normal':
                        normalPlugins.push(plugin);
                        break;
                    default:
                        topPlugins.push(plugin);
                }
            }
        }
        return {
            topPlugins:    topPlugins,
            normalPlugins: normalPlugins,
            lockedPlugins: lockedPlugins
        };
    },
    
    destroyLockable: function(){
        
        Ext.destroy(this.view, this.headerCt);
    }
}, function() {
    this.borrow(Ext.AbstractComponent, ['constructPlugin']);
});


Ext.define('Ext.tree.View', {
    extend:  Ext.view.Table ,
    alias: 'widget.treeview',

               
                            
      

    
    isTreeView: true,

    loadingCls: Ext.baseCSSPrefix + 'grid-tree-loading',
    expandedCls: Ext.baseCSSPrefix + 'grid-tree-node-expanded',
    leafCls: Ext.baseCSSPrefix + 'grid-tree-node-leaf',

    expanderSelector: '.' + Ext.baseCSSPrefix + 'tree-expander',
    checkboxSelector: '.' + Ext.baseCSSPrefix + 'tree-checkbox',
    expanderIconOverCls: Ext.baseCSSPrefix + 'tree-expander-over',

    
    
    
    nodeAnimWrapCls: Ext.baseCSSPrefix + 'tree-animator-wrap',
    
    ariaRole: 'tree',

    blockRefresh: true,

    
    loadMask: false,

    
    rootVisible: true,

    
    deferInitialRefresh: false,

    

    expandDuration: 250,
    collapseDuration: 250,

    toggleOnDblClick: true,

    stripeRows: false,

    
    uiFields: ['expanded', 'loaded', 'checked', 'expandable', 'leaf', 'icon', 'iconCls', 'loading', 'qtip', 'qtitle'],

    
    treeRowTpl: [
        '{%',
            'this.processRowValues(values);',
            'this.nextTpl.applyOut(values, out, parent);',
        '%}', {
            priority: 10,
            processRowValues: function(rowValues) {
                var record = rowValues.record,
                    view = rowValues.view;

                rowValues.rowAttr = {};
                
                
                
                rowValues.rowAttr['data-qtip'] = record.get('qtip') || '';
                rowValues.rowAttr['data-qtitle'] = record.get('qtitle') || '';
                if (record.isExpanded()) {
                    rowValues.rowClasses.push(view.expandedCls);
                }
                if (record.isLeaf()) {
                    rowValues.rowClasses.push(view.leafCls);
                }
                if (record.isLoading()) {
                    rowValues.rowClasses.push(view.loadingCls);
                }
            }
        }
    ],

    initComponent: function() {
        var me = this,
            treeStore = me.panel.getStore(),
            store = me.store;

        if (me.initialConfig.animate === undefined) {
            me.animate = Ext.enableFx;
        }

        if (!store || store === treeStore) {
            me.store = store = new Ext.data.NodeStore({
                treeStore: treeStore,
                recursive: true,
                rootVisible: me.rootVisible
            });
        }

        if (me.node) {
            me.setRootNode(me.node);
        }
        me.animQueue = {};
        me.animWraps = {};
        me.addEvents(
            
            'afteritemexpand',
            
            'afteritemcollapse',
            
            'nodedragover'
        );
        me.callParent(arguments);
        me.addRowTpl(Ext.XTemplate.getTpl(me, 'treeRowTpl'));
    },

    onBeforeFill: function(treeStore, fillRoot) {
        this.store.suspendEvents();
    },

    onFillComplete: function(treeStore, fillRoot, newNodes) {
        var me = this,
            store = me.store,
            start = store.indexOf(newNodes[0]);

        store.resumeEvents();

        
        
        fillRoot.triggerUIUpdate();

        
        
        if (!newNodes.length || start === -1) {
            return;
        }

        
        me.onAdd(me.store, newNodes, start);

        me.refreshPartner();
    },

    onBeforeSort: function() {
        this.store.suspendEvents(); 
    },

    onSort: function(o) {
        
        
        if (o.isStore) {
            this.store.resumeEvents();
            this.refresh();
            this.refreshPartner();
        }
    },

    refreshPartner: function() {
        var partner = this.lockingPartner;
        if (partner) {
            partner.refresh();
        }
    },

    getMaskStore: function() {
        return this.panel.getStore();
    },

    afterRender: function() {
        var me = this;
        me.callParent(arguments);

        me.el.on({
            scope: me,
            delegate: me.expanderSelector,
            mouseover: me.onExpanderMouseOver,
            mouseout: me.onExpanderMouseOut,
            click: {
                delegate: me.checkboxSelector,
                fn: me.onCheckboxChange,
                scope: me
            }
        });
    },

    afterComponentLayout: function() {
        this.callParent(arguments);
        var stretcher = this.stretcher;
        if (stretcher) {
            stretcher.setWidth((this.getWidth() - Ext.getScrollbarSize().width));
        }
    },

    processUIEvent: function(e) {
        
        
        
        if (e.getTarget('.' + this.nodeAnimWrapCls, this.el)) {
            return false;
        }
        return this.callParent(arguments);
    },

    onClear: function() {
        this.store.removeAll();
    },

    setRootNode: function(node) {
        var me = this;
        me.store.setNode(node);
        me.node = node;
    },

    onCheckboxChange: function(e, t) {
        var me = this,
            item = e.getTarget(me.getItemSelector(), me.getTargetEl());

        if (item) {
            me.onCheckChange(me.getRecord(item));
        }
    },

    onCheckChange: function(record) {
        var checked = record.get('checked');
        if (Ext.isBoolean(checked)) {
            checked = !checked;
            record.set('checked', checked);
            this.fireEvent('checkchange', record, checked);
        }
    },

    getChecked: function() {
        var checked = [];
        this.node.cascadeBy(function(rec){
            if (rec.get('checked')) {
                checked.push(rec);
            }
        });
        return checked;
    },

    isItemChecked: function(rec) {
        return rec.get('checked');
    },

    
    createAnimWrap: function(record, index) {
        var me = this,
            node = me.getNode(record),
            tmpEl, nodeEl,
            columnSizer = [];

        me.renderColumnSizer(columnSizer);
        nodeEl = Ext.get(node);
        tmpEl = nodeEl.insertSibling({
            role: 'presentation',
            tag: 'tr',
            html: [
                '<td colspan="' + me.panel.headerCt.getColumnCount() + '" role="presentation">',
                    '<div class="' + me.nodeAnimWrapCls + '" role="presentation">',
                        
                        '<table class="' + Ext.baseCSSPrefix + me.id + '-table ' + Ext.baseCSSPrefix + 'grid-table" style="border:0" cellspacing="0" cellpadding="0" role="presentation">',
                        columnSizer.join(''),
                        '<tbody></tbody></table>',
                    '</div>',
                '</td>'
            ].join('')
        }, 'after');

        return {
            record: record,
            node: node,
            el: tmpEl,
            expanding: false,
            collapsing: false,
            animating: false,
            animateEl: tmpEl.down('div'),
            targetEl: tmpEl.down('tbody')
        };
    },

    
    getAnimWrap: function(parent, bubble) {
        if (!this.animate) {
            return null;
        }

        var wraps = this.animWraps,
            wrap = wraps[parent.internalId];

        if (bubble !== false) {
            while (!wrap && parent) {
                parent = parent.parentNode;
                if (parent) {
                    wrap = wraps[parent.internalId];
                }
            }
        }
        return wrap;
    },

    doAdd: function(records, index) {
        
        
        var me = this,
            nodes = me.bufferRender(records, index, true),
            record = records[0],
            parent = record.parentNode,
            all = me.all,
            relativeIndex,
            animWrap = me.getAnimWrap(parent),
            targetEl, children, len;

        if (!animWrap || !animWrap.expanding) {
            return me.callParent(arguments);
        }

        
        parent = animWrap.record;

        
        targetEl = animWrap.targetEl;
        children = targetEl.dom.childNodes;
        len = children.length;

        
        relativeIndex = index - me.indexInStore(parent) - 1;

        
        
        if (!len || relativeIndex >= len) {
            targetEl.appendChild(nodes);
        }
        
        
        else {
            Ext.fly(children[relativeIndex]).insertSibling(nodes, 'before', true);
        }

        
        all.insert(index, nodes);

        
        
        if (animWrap.isAnimating) {
            me.onExpand(parent);
        }
    },

    onRemove : function(ds, records, indexes) {
        var me = this,
            empty, i;

        if (me.viewReady) {
            empty = me.store.getCount() === 0;

            
            if (empty) {
                me.refresh();
            }
            else {
                
                for (i = indexes.length - 1; i >= 0; --i) {
                    me.doRemove(records[i], indexes[i]);
                }
            }

            
            if (me.hasListeners.itemremove) {
                for (i = indexes.length - 1; i >= 0; --i) {
                    me.fireEvent('itemremove', records[i], indexes[i]);
                }
            }
        }
    },

    doRemove: function(record, index) {
        
        
        var me = this,
            all = me.all,
            animWrap = me.getAnimWrap(record),
            item = all.item(index),
            node = item ? item.dom : null;

        if (!node || !animWrap || !animWrap.collapsing) {
            return me.callParent(arguments);
        }

        
        
        animWrap.targetEl.dom.insertBefore(node, animWrap.targetEl.dom.firstChild);
        all.removeElement(index);
    },

    onBeforeExpand: function(parent, records, index) {
        var me = this,
            animWrap;

        if (me.rendered && me.all.getCount() && me.animate) {
            if (me.getNode(parent)) {
                animWrap = me.getAnimWrap(parent, false);
                if (!animWrap) {
                    animWrap = me.animWraps[parent.internalId] = me.createAnimWrap(parent);
                    animWrap.animateEl.setHeight(0);
                }
                else if (animWrap.collapsing) {
                    
                    
                    animWrap.targetEl.select(me.itemSelector).remove();
                }
                animWrap.expanding = true;
                animWrap.collapsing = false;
            }
        }
    },

    onExpand: function(parent) {
        var me = this,
            queue = me.animQueue,
            id = parent.getId(),
            node = me.getNode(parent),
            index = node ? me.indexOf(node) : -1,
            animWrap,
            animateEl,
            targetEl,
            fromHeight = Ext.isIEQuirks ? 1 : 0 

        if (me.singleExpand) {
            me.ensureSingleExpand(parent);
        }

        
        if (index === -1) {
            return;
        }

        animWrap = me.getAnimWrap(parent, false);

        if (!animWrap) {
            me.refreshSelection();
            parent.isExpandingOrCollapsing = false;
            me.fireEvent('afteritemexpand', parent, index, node);
            me.refreshSize();
            return;
        }

        animateEl = animWrap.animateEl;
        targetEl = animWrap.targetEl;

        animateEl.stopAnimation();
        queue[id] = true;

        
        animateEl.dom.style.height = fromHeight + 'px';
        animateEl.animate({
            from: {
                height: fromHeight
            },
            to: {
                height: targetEl.getHeight()
            },
            duration: me.expandDuration,
            listeners: {
                afteranimate: function() {
                    
                    
                    
                    var items = targetEl.query(me.itemSelector);
                    if (items.length) {
                        animWrap.el.insertSibling(items, 'before', true);
                    }
                    animWrap.el.remove();
                    me.refreshSize();
                    delete me.animWraps[animWrap.record.internalId];
                    delete queue[id];
                }
            },
            callback: function() {
                me.refreshSelection();
                parent.isExpandingOrCollapsing = false;
                me.fireEvent('afteritemexpand', parent, index, node);
            }
        });

        animWrap.isAnimating = true;
    },

    
    onBeforeCollapse: function(parent, records, index, callback, scope) {
        var me = this,
            animWrap;

        if (me.rendered && me.all.getCount()) {
            if (me.animate) {
                
                
                
                if (Ext.Array.contains(parent.stores, me.store)) {
                    animWrap = me.getAnimWrap(parent);
                    if (!animWrap) {
                        animWrap = me.animWraps[parent.internalId] = me.createAnimWrap(parent, index);
                    }
                    else if (animWrap.expanding) {
                        
                        
                        animWrap.targetEl.select(this.itemSelector).remove();
                    }
                    animWrap.expanding = false;
                    animWrap.collapsing = true;
                    animWrap.callback = callback;
                    animWrap.scope = scope;
                }
            } else {
                
                me.onCollapseCallback = callback;
                me.onCollapseScope = scope;
            }
            
            
            me.onRowDeselect(me.indexOf(parent.firstChild));
        }
    },

    onCollapse: function(parent) {
        var me = this,
            queue = me.animQueue,
            id = parent.getId(),
            node = me.getNode(parent),
            index = node ? me.indexOf(node) : -1,
            animWrap = me.getAnimWrap(parent),
            animateEl;

        
        
        
        if (!me.all.getCount() || !Ext.Array.contains(parent.stores, me.store)) {
            return;
        }

        
        if (!animWrap) {
            me.refreshSelection();
            parent.isExpandingOrCollapsing = false;
            me.fireEvent('afteritemcollapse', parent, index, node);
            me.refreshSize();

            
            Ext.callback(me.onCollapseCallback, me.onCollapseScope);
            me.onCollapseCallback = me.onCollapseScope = null;
            return;
        }

        animateEl = animWrap.animateEl;

        queue[id] = true;

        animateEl.stopAnimation();
        animateEl.animate({
            to: {
                height: Ext.isIEQuirks ? 1 : 0 
            },
            duration: me.collapseDuration,
            listeners: {
                afteranimate: function() {
                    
                    animWrap.el.remove();
                    me.refreshSize();
                    delete me.animWraps[animWrap.record.internalId];
                    delete queue[id];
                }
            },
            callback: function() {
                me.refreshSelection();
                parent.isExpandingOrCollapsing = false;
                me.fireEvent('afteritemcollapse', parent, index, node);

                
                Ext.callback(animWrap.callback, animWrap.scope);
                animWrap.callback = animWrap.scope = null;
            }
        });
        animWrap.isAnimating = true;
    },

    
    isAnimating: function(node) {
        return !!this.animQueue[node.getId()];
    },

    
    expand: function(record, deep, callback, scope) {
        var me = this,
            doAnimate = !!me.animate,
            result;

        
        if (!doAnimate || !record.isExpandingOrCollapsing) {
            if (!record.isLeaf()) {
                record.isExpandingOrCollapsing = doAnimate;
            }

            
            
            
            Ext.suspendLayouts();
            result = record.expand(deep, callback, scope);
            Ext.resumeLayouts(true);
            return result;
        }
    },

    
    collapse: function(record, deep, callback, scope) {
        var me = this,
            doAnimate = !!me.animate;

        
        if (!doAnimate || !record.isExpandingOrCollapsing) {
            if (!record.isLeaf()) {
                record.isExpandingOrCollapsing = doAnimate;
            }
            return record.collapse(deep, callback, scope);
        }
    },

    
    toggle: function(record, deep, callback, scope) {
        if (record.isExpanded()) {
            this.collapse(record, deep, callback, scope);
        } else {
            this.expand(record, deep, callback, scope);
        }
    },

    onItemDblClick: function(record, item, index) {
        var me = this,
            editingPlugin = me.editingPlugin;
            
        me.callParent(arguments);
        if (me.toggleOnDblClick && record.isExpandable() && !(editingPlugin && editingPlugin.clicksToEdit === 2)) {
            me.toggle(record);
        }
    },

    onBeforeItemMouseDown: function(record, item, index, e) {
        if (e.getTarget(this.expanderSelector, item)) {
            return false;
        }
        return this.callParent(arguments);
    },

    onItemClick: function(record, item, index, e) {
        if (e.getTarget(this.expanderSelector, item) && record.isExpandable()) {
            this.toggle(record, e.ctrlKey);
            return false;
        }
        return this.callParent(arguments);
    },

    onExpanderMouseOver: function(e, t) {
        e.getTarget(this.cellSelector, 10, true).addCls(this.expanderIconOverCls);
    },

    onExpanderMouseOut: function(e, t) {
        e.getTarget(this.cellSelector, 10, true).removeCls(this.expanderIconOverCls);
    },
    
    getStoreListeners: function(){
        var me = this,
            listeners = me.callParent(arguments);
            
        return Ext.apply(listeners, {
            beforeexpand: me.onBeforeExpand,
            expand: me.onExpand,
            beforecollapse: me.onBeforeCollapse,
            collapse: me.onCollapse,
            write: me.onStoreWrite,
            datachanged: me.onStoreDataChanged
        });    
    },
    
    onBindStore: function(){
        var me = this,
            treeStore = me.getTreeStore();
        
        me.callParent(arguments);
        
        me.mon(treeStore, {
            scope: me,
            beforefill: me.onBeforeFill,
            fillcomplete: me.onFillComplete
        });

        if (!treeStore.remoteSort) {
            
            
            me.mon(treeStore, {
                scope: me,
                beforesort: me.onBeforeSort,
                sort: me.onSort
            });
        }
    },
    
    onUnbindStore: function(){
        var me = this,
            treeStore = me.getTreeStore();
            
        me.callParent(arguments);
        
        me.mun(treeStore, {
            scope: me,
            beforefill: me.onBeforeFill,
            fillcomplete: me.onFillComplete
        });

        if (!treeStore.remoteSort) {
            me.mun(treeStore, {
                scope: me,
                beforesort: me.onBeforeSort,
                sort: me.onSort
            });
        }
    },

    
    getTreeStore: function() {
        return this.panel.store;
    },

    ensureSingleExpand: function(node) {
        var parent = node.parentNode;
        if (parent) {
            parent.eachChild(function(child) {
                if (child !== node && child.isExpanded()) {
                    child.collapse();
                }
            });
        }
    },

    shouldUpdateCell: function(record, column, changedFieldNames){
        if (changedFieldNames) {
            var i = 0,
                len = changedFieldNames.length;

            for (; i < len; ++i) {
                if (Ext.Array.contains(this.uiFields, changedFieldNames[i])) {
                    return true;
                }
            }
        }
        return this.callParent(arguments);
    },

    
    onStoreWrite: function(store, operation) {
        var treeStore = this.panel.store;
        treeStore.fireEvent('write', treeStore, operation);
    },

    
    onStoreDataChanged: function(store, operation) {
        var treeStore = this.panel.store;
        treeStore.fireEvent('datachanged', treeStore);
    }
});


Ext.define('Ext.grid.plugin.BufferedRendererTreeView', {
    override: 'Ext.tree.View',

    onRemove: function(store, records, indices) {
        var me = this;
        
        
        if (me.rendered && me.bufferedRenderer) {
            me.refreshView();
        }
        
        else {
            me.callParent([store, records, indices]);
        }
    }    
});


Ext.define('Ext.grid.plugin.BufferedRenderer', {
    extend:  Ext.AbstractPlugin ,
               
                                                    
                                                  
      
    alias: 'plugin.bufferedrenderer',
    lockableScope: 'both',

    
    percentageFromEdge: 0.35,

    
    variableRowHeight: false,

    
    numFromEdge: 8,

    
    trailingBufferZone: 10,

    
    leadingBufferZone: 20,

    
    synchronousRender: true,

    
    scrollToLoadBuffer: 200,

    
    viewSize: 0,
    
    rowHeight: 21,
    
    position: 0,
    lastScrollDirection: 1,
    bodyTop: 0,

    
    init: function(grid) {
        var me = this,
            view = grid.view,
            viewListeners = {
                scroll: {
                    fn: me.onViewScroll,
                    element: 'el',
                    scope: me
                },
                boxready: me.onViewResize,
                resize: me.onViewResize,
                refresh: me.onViewRefresh,
                scope: me,
                destroyable: true
            };

        
        if (!me.variableRowHeight && grid.ownerLockable) {
            grid.ownerLockable.syncRowHeight = false;
        }

        
        
        if (grid.isTree || grid.ownerLockable && grid.ownerLockable.isTree) {
            view.blockRefresh = false;
            view.loadMask = true;
        }
        if (view.positionBody) {
            viewListeners.refresh = me.onViewRefresh;
        }
        me.grid = grid;
        me.view = view;
        view.bufferedRenderer = me;
        view.preserveScrollOnRefresh = true;

        me.bindStore(view.dataSource);
        view.getViewRange = function() {
            return me.getViewRange();
        };

        me.position = 0;

        me.gridListeners = grid.on('reconfigure', me.onReconfigure, me);
        me.viewListeners = view.on(viewListeners);
    },

    bindStore: function(store) {
        var me = this;
        if (me.store) {
            me.unbindStore();
        }
        me.storeListeners = store.on({
            scope: me,
            clear: me.onStoreClear,
            destroyable: true
        });
        me.store = store;

        
        if (me.view.componentLayout.layoutCount) {
            me.onViewResize(me.view, 0, me.view.getHeight());
        }
    },

    onReconfigure: function(grid, store){
        if (store && store !== this.store) {
            this.bindStore(store);
        }
    },

    unbindStore: function() {
        this.storeListeners.destroy();
        this.store = null;
    },

    onStoreClear: function() {
        var me = this;

        
        if (me.view.rendered && !me.store.isDestroyed) {
            
            
            if (me.scrollTop !== 0) {
                me.ignoreNextScrollEvent = true;
                me.view.el.dom.scrollTop = 0;
            }

            me.bodyTop = me.scrollTop = me.position = me.scrollHeight = 0;
            me.lastScrollDirection = me.scrollOffset = null;

            
            delete me.rowHeight;
        }
    },

    onViewRefresh: function() {
        var me = this,
            view = me.view,
            oldScrollHeight = me.scrollHeight,
            scrollHeight;

        
        if (view.all.getCount()) {
            
            
            delete me.rowHeight;
        }

        
        
        scrollHeight = me.getScrollHeight();

        if (!oldScrollHeight || scrollHeight != oldScrollHeight) {
            me.stretchView(view, scrollHeight);
        }

        if (me.scrollTop !== view.el.dom.scrollTop) {
            
            
            
            me.onViewScroll();    
        } else {
            me.setBodyTop(me.bodyTop);

            
            if (view.all.getCount()) {
                me.viewSize = 0;
                me.onViewResize(view, null, view.getHeight());
            }
        }
    },

    onViewResize: function(view, width, height, oldWidth, oldHeight) {
        
        if (!oldHeight || height !== oldHeight) {
            var me = this,
                newViewSize;

            
            newViewSize = Math.ceil(height / me.rowHeight) + me.trailingBufferZone + me.leadingBufferZone;
            me.viewSize = me.setViewSize(newViewSize);
        }
    },
 
    stretchView: function(view, scrollRange) {
        var me = this,
            recordCount = (me.store.buffered ? me.store.getTotalCount() : me.store.getCount());

        if (me.stretcher) {
            me.stretcher.dom.style.marginTop = (scrollRange - 1) + 'px';
        } else {
            var el = view.el;

            
            
            if (view.refreshCounter) {
                view.fixedNodes++;
            }

            
            if (recordCount && (me.view.all.endIndex === recordCount - 1)) {
                scrollRange = me.bodyTop + view.body.dom.offsetHeight;
            }
            this.stretcher = el.createChild({
                style: {
                    width: '1px',
                    height: '1px',
                    'marginTop': (scrollRange - 1) + 'px',
                    left: 0,
                    position: 'absolute'
                }
            }, el.dom.firstChild);
        }
    },

    setViewSize: function(viewSize) {
        if (viewSize !== this.viewSize) {

            
            this.scrollTop = this.view.el.dom.scrollTop;

            var me = this,
                store = me.store,
                elCount = me.view.all.getCount(),
                start, end,
                lockingPartner = me.lockingPartner;

                me.viewSize = store.viewSize = viewSize;

            
            
            if (elCount) {
                start = me.view.all.startIndex;
                    end = Math.min(start + viewSize - 1, (store.buffered ? store.getTotalCount() : store.getCount()) - 1);

                
                if (lockingPartner) {
                    lockingPartner.disable();
                }
                me.renderRange(start, end);
                if (lockingPartner) {
                    lockingPartner.enable();
                }
            }
        }
        return viewSize;
    },

    getViewRange: function() {
        var me = this,
            rows = me.view.all,
            store = me.store;

        if (store.data.getCount()) {
            return store.getRange(rows.startIndex, rows.startIndex + (me.viewSize || store.defaultViewSize) - 1);
        } else {
            return [];
        }
    },

    
    scrollTo: function(recordIdx, doSelect, callback, scope) {
        var me = this,
            view = me.view,
            viewDom = view.el.dom,
            store = me.store,
            total = store.buffered ? store.getTotalCount() : store.getCount(),
            startIdx, endIdx,
            targetRec,
            targetRow,
            tableTop,
            groupingFeature,
            group,
            record;

        
        
        
        if ((groupingFeature = view.dataSource.groupingFeature) && (groupingFeature.collapsible !== false)) {

            
            recordIdx = Math.min(Math.max(recordIdx, 0), view.store.getCount() - 1);
            record = view.store.getAt(recordIdx);
            recordIdx = groupingFeature.indexOf(recordIdx);
            group = groupingFeature.getGroup(record);

            if (group.isCollapsed) {
                groupingFeature.expand(group.name);
                total = store.buffered ? store.getTotalCount() : store.getCount();
            }

        } else {

            
            recordIdx = Math.min(Math.max(recordIdx, 0), total - 1);
        }

        
        startIdx = Math.max(Math.min(recordIdx - ((me.leadingBufferZone + me.trailingBufferZone) / 2), total - me.viewSize + 1), 0);
        tableTop = startIdx * me.rowHeight;
        endIdx = Math.min(startIdx + me.viewSize - 1, total - 1);

        store.getRange(startIdx, endIdx, {
            callback: function(range, start, end) {

                me.renderRange(start, end, true);

                targetRec = store.data.getRange(recordIdx, recordIdx)[0];
                targetRow = view.getNode(targetRec, false);
                view.body.dom.style.top = tableTop + 'px';
                me.position = me.scrollTop = viewDom.scrollTop = tableTop = Math.min(Math.max(0, tableTop - view.body.getOffsetsTo(targetRow)[1]), viewDom.scrollHeight - viewDom.clientHeight);

                
                if (Ext.isIE) {
                    viewDom.scrollTop = tableTop;
                }
                if (doSelect) {
                    view.selModel.select(targetRec);
                }
                if (callback) {
                    callback.call(scope||me, recordIdx, targetRec);
                }
            }
        });
    },

    onViewScroll: function(e, t) {
        var me = this,
            store = me.store,
            totalCount = (store.buffered ? store.getTotalCount() : store.getCount()),
            vscrollDistance,
            scrollDirection,
            scrollTop = me.scrollTop = me.view.el.dom.scrollTop,
            scrollHandled = false;

        
        
        if (me.ignoreNextScrollEvent) {
            me.ignoreNextScrollEvent = false;
            return;
        }

        
        
        if (!(me.disabled || totalCount < me.viewSize)) {

            vscrollDistance = scrollTop - me.position;
            scrollDirection = vscrollDistance > 0 ? 1 : -1;

            
            if (Math.abs(vscrollDistance) >= 20 || (scrollDirection !== me.lastScrollDirection)) {
                me.lastScrollDirection = scrollDirection;
                me.handleViewScroll(me.lastScrollDirection);
                scrollHandled = true;
            }
        }

        
        if (!scrollHandled) {
            if (me.lockingPartner && me.lockingPartner.scrollTop !== scrollTop) {
                me.lockingPartner.view.el.dom.scrollTop = scrollTop;
            }
        }
    },

    handleViewScroll: function(direction) {
        var me                = this,
            rows              = me.view.all,
            store             = me.store,
            viewSize          = me.viewSize,
            totalCount        = (store.buffered ? store.getTotalCount() : store.getCount()),
            requestStart,
            requestEnd;

        
        if (direction == -1) {

            
            if (rows.startIndex) {
                if ((me.getFirstVisibleRowIndex() - rows.startIndex) < me.numFromEdge) {
                    requestStart = Math.max(0, me.getLastVisibleRowIndex() + me.trailingBufferZone - viewSize);
                }
            }
        }
        
        else {

            
            if (rows.endIndex < totalCount - 1) {
                if ((rows.endIndex - me.getLastVisibleRowIndex()) < me.numFromEdge) {
                    requestStart = Math.max(0, me.getFirstVisibleRowIndex() - me.trailingBufferZone);
                }
            }
        }

        
        if (requestStart != null) {
            requestEnd = Math.min(requestStart + viewSize - 1, totalCount - 1);

            
            if (requestStart !== rows.startIndex || requestEnd !== rows.endIndex) {
                me.renderRange(requestStart, requestEnd);
                return;
            }
        }

        
        if (me.lockingPartner && me.lockingPartner.view.el && me.lockingPartner.scrollTop !== me.scrollTop) {
            me.lockingPartner.view.el.dom.scrollTop = me.scrollTop;
        }
    },

    renderRange: function(start, end, forceSynchronous) {
        var me = this,
            rows = me.view.all,
            store = me.store;

        
        
        
        if (!(start === rows.startIndex && end === rows.endIndex)) {

            
            if (store.rangeCached(start, end)) {
                me.cancelLoad();

                if (me.synchronousRender || forceSynchronous) {
                    me.onRangeFetched(null, start, end);
                } else {
                    if (!me.renderTask) {
                        me.renderTask = new Ext.util.DelayedTask(me.onRangeFetched, me, null, false);
                    }
                    
                    
                    
                    
                    me.renderTask.delay(1, null, null, [null, start, end]);
                }
            }

            
            else {
                me.attemptLoad(start, end);
            }
        }
    },

    onRangeFetched: function(range, start, end, fromLockingPartner) {
        var me = this,
            view = me.view,
            oldStart,
            rows = view.all,
            removeCount,
            increment = 0,
            calculatedTop = start * me.rowHeight,
            top,
            lockingPartner = me.lockingPartner;

        
        if (view.isDestroyed) {
            return;
        }

        
        if (!range) {
            range = me.store.getRange(start, end);

            
            if (!range) {
                return;
            }
        }

        
        if (start > rows.endIndex || end < rows.startIndex) {
            rows.clear(true);
            top = calculatedTop;
        }

        if (!rows.getCount()) {
            view.doAdd(range, start);
        }
        
        else if (end > rows.endIndex) {
            removeCount = Math.max(start - rows.startIndex, 0);

            
            if (me.variableRowHeight) {
                increment = rows.item(rows.startIndex + removeCount, true).offsetTop;
            }
            rows.scroll(Ext.Array.slice(range, rows.endIndex + 1 - start), 1, removeCount, start, end);

            
            if (me.variableRowHeight) {
                
                top = me.bodyTop + increment;
            } else {
                top = calculatedTop;
            }
        }
        
        else {
            removeCount = Math.max(rows.endIndex - end, 0);
            oldStart = rows.startIndex;
            rows.scroll(Ext.Array.slice(range, 0, rows.startIndex - start), -1, removeCount, start, end);

            
            if (me.variableRowHeight) {
                
                top = me.bodyTop - rows.item(oldStart, true).offsetTop;
            } else {
                top = calculatedTop;
            }
        }
        
        
        me.position = me.scrollTop;

        
        
        if (view.positionBody) {
            me.setBodyTop(top, calculatedTop);
        }

        
        
        if (lockingPartner && !lockingPartner.disabled && !fromLockingPartner) {
            lockingPartner.onRangeFetched(range, start, end, true);
            if (lockingPartner.scrollTop !== me.scrollTop) {
                lockingPartner.view.el.dom.scrollTop = me.scrollTop;
            }
        }
    },

    setBodyTop: function(bodyTop, calculatedTop) {
        var me = this,
            view = me.view,
            store = me.store,
            body = view.body.dom,
            delta;

        bodyTop = Math.floor(bodyTop);

        
        
        
        if (calculatedTop !== undefined) {
            delta = bodyTop - calculatedTop;
            bodyTop = calculatedTop;
        }
        body.style.position = 'absolute';
        body.style.top = (me.bodyTop = bodyTop) + 'px';

        
        
        if (delta) {
            me.scrollTop = me.position = view.el.dom.scrollTop -= delta;
        }

        
        if (view.all.endIndex === (store.buffered ? store.getTotalCount() : store.getCount()) - 1) {
            me.stretchView(view, me.bodyTop + body.offsetHeight);
        }
    },

    getFirstVisibleRowIndex: function(startRow, endRow, viewportTop, viewportBottom) {
        var me = this,
            view = me.view,
            rows = view.all,
            elements = rows.elements,
            clientHeight = view.el.dom.clientHeight,
            target,
            targetTop;

        
        if (rows.getCount() && me.variableRowHeight) {
            if (!arguments.length) {
                startRow = rows.startIndex;
                endRow = rows.endIndex;
                viewportTop = me.scrollTop;
                viewportBottom = viewportTop + clientHeight;

                
                if (me.bodyTop > viewportBottom || me.bodyTop + view.body.getHeight() < viewportTop) {
                    return Math.floor(me.scrollTop / me.rowHeight);
                }

                
                target = startRow + Math.min(me.numFromEdge + ((me.lastScrollDirection == -1) ? me.leadingBufferZone : me.trailingBufferZone), Math.floor((endRow - startRow) / 2));
            } else {
                target = startRow + Math.floor((endRow - startRow) / 2);
            }
            targetTop = me.bodyTop + elements[target].offsetTop;

            
            if (targetTop + elements[target].offsetHeight < viewportTop) {
                return me.getFirstVisibleRowIndex(target + 1, endRow, viewportTop, viewportBottom);
            }
            
            
            if (targetTop <= viewportTop) {
                return target;
            }
            
            else if (target !== startRow) {
                return me.getFirstVisibleRowIndex(startRow, target - 1, viewportTop, viewportBottom);
            }
        }
        return Math.floor(me.scrollTop / me.rowHeight);
    },

    getLastVisibleRowIndex: function(startRow, endRow, viewportTop, viewportBottom) {
        var me = this,
            view = me.view,
            rows = view.all,
            elements = rows.elements,
            clientHeight = view.el.dom.clientHeight,
            target,
            targetTop, targetBottom;

        
        if (rows.getCount() && me.variableRowHeight) {
            if (!arguments.length) {
                startRow = rows.startIndex;
                endRow = rows.endIndex;
                viewportTop = me.scrollTop;
                viewportBottom = viewportTop + clientHeight;

                
                if (me.bodyTop > viewportBottom || me.bodyTop + view.body.getHeight() < viewportTop) {
                    return Math.floor(me.scrollTop / me.rowHeight) + Math.ceil(clientHeight / me.rowHeight);
                }

                
                target = endRow - Math.min(me.numFromEdge + ((me.lastScrollDirection == 1) ? me.leadingBufferZone : me.trailingBufferZone), Math.floor((endRow - startRow) / 2));
            } else {
                target = startRow + Math.floor((endRow - startRow) / 2);
            }
            targetTop = me.bodyTop + elements[target].offsetTop;

            
            if (targetTop > viewportBottom) {
                return me.getLastVisibleRowIndex(startRow, target - 1, viewportTop, viewportBottom);
            }
            targetBottom = targetTop + elements[target].offsetHeight;

            
            if (targetBottom >= viewportBottom) {
                return target;
            }
            
            else if (target !== endRow) {
                return me.getLastVisibleRowIndex(target + 1, endRow, viewportTop, viewportBottom);
            }
        }
        return me.getFirstVisibleRowIndex() + Math.ceil(clientHeight / me.rowHeight);
    },

    getScrollHeight: function() {
        var me = this,
            view   = me.view,
            store  = me.store,
            doCalcHeight = !me.hasOwnProperty('rowHeight'),
            storeCount = me.store.getCount();

        if (!storeCount) {
            return 0;
        }
        if (doCalcHeight) {
            if (view.all.getCount()) {
                me.rowHeight = Math.floor(view.body.getHeight() / view.all.getCount());
            }
        }
        return this.scrollHeight = Math.floor((store.buffered ? store.getTotalCount() : store.getCount()) * me.rowHeight);
    },

    attemptLoad: function(start, end) {
        var me = this;
        if (me.scrollToLoadBuffer) {
            if (!me.loadTask) {
                me.loadTask = new Ext.util.DelayedTask(me.doAttemptLoad, me, []);
            }
            me.loadTask.delay(me.scrollToLoadBuffer, me.doAttemptLoad, me, [start, end]);
        } else {
            me.store.getRange(start, end, {
                callback: me.onRangeFetched,
                scope: me,
                fireEvent: false
            });
        }
    },

    cancelLoad: function() {
        if (this.loadTask) {
            this.loadTask.cancel();
        }
    },

    doAttemptLoad:  function(start, end) {
        this.store.getRange(start, end, {
            callback: this.onRangeFetched,
            scope: this,
            fireEvent: false
        });
    },

    destroy: function() {
        var me = this,
            view = me.view;

        if (view && view.el) {
            view.el.un('scroll', me.onViewScroll, me); 
        }

        
        Ext.destroy(me.viewListeners, me.storeListeners, me.gridListeners);
    }
});


Ext.define('Ext.grid.plugin.Editing', {
    alias: 'editing.editing',
    extend:  Ext.AbstractPlugin ,

               
                                 
                         
      

    mixins: {
        observable:  Ext.util.Observable 
    },

    
    clicksToEdit: 2,

    
    triggerEvent: undefined,

    

    relayedEvents: [
        'beforeedit',
        'edit',
        'validateedit',
        'canceledit'
    ],

    
    defaultFieldXType: 'textfield',

    
    editStyle: '',

    constructor: function(config) {
        var me = this;

        me.addEvents(
            
            'beforeedit',

            
            'edit',

            
            'validateedit',
            
            'canceledit'

        );
        me.callParent(arguments);
        me.mixins.observable.constructor.call(me);
        
        me.on("edit", function(editor, e) {
            me.fireEvent("afteredit", editor, e);
        });
    },

    
    init: function(grid) {
        var me = this;

        me.grid = grid;
        me.view = grid.view;
        me.initEvents();

        
        me.mon(grid, {
            beforereconfigure: me.onBeforeReconfigure,
            reconfigure: me.onReconfigure,
            scope: me,
            beforerender: {
                fn: me.onReconfigure,
                single: true,
                scope: me
            }
        });

        grid.relayEvents(me, me.relayedEvents);

        
        if (me.grid.ownerLockable) {
            me.grid.ownerLockable.relayEvents(me, me.relayedEvents);
        }
        
        
        grid.isEditable = true;
        grid.editingPlugin = grid.view.editingPlugin = me;
    },
    
    onBeforeReconfigure: function() {
        this.reconfiguring = true;
    },

    
    onReconfigure: function() {
        
        
        this.initFieldAccessors(this.grid.getTopLevelColumnManager().getColumns());
        delete this.reconfiguring;
    },

    
    destroy: function() {
        var me = this,
            grid = me.grid;

        Ext.destroy(me.keyNav);
        
        me.clearListeners();

        if (grid) {
            grid.editingPlugin = grid.view.editingPlugin = me.grid = me.view = me.editor = me.keyNav = null;
        }
    },

    
    getEditStyle: function() {
        return this.editStyle;
    },

    
    initFieldAccessors: function(columns) {
        
        if (columns.isGroupHeader) {
            columns = columns.getGridColumns();
        }

        
        else if (!Ext.isArray(columns)) {
            columns = [columns];
        }

        var me   = this,
            c,
            cLen = columns.length,
            column;

        for (c = 0; c < cLen; c++) {
            column = columns[c];

            if (!column.getEditor) {
                column.getEditor = function(record, defaultField) {
                    return me.getColumnField(this, defaultField);
                };
            }
            if (!column.hasEditor) {
                column.hasEditor = function() {
                    return me.hasColumnField(this);
                };
            }
            if (!column.setEditor) {
                column.setEditor = function(field) {
                    me.setColumnField(this, field);
                };
            }
        }
    },

    
    removeFieldAccessors: function(columns) {
        
        if (columns.isGroupHeader) {
            columns = columns.getGridColumns();
        }

        
        else if (!Ext.isArray(columns)) {
            columns = [columns];
        }

        var c,
            cLen = columns.length,
            column;

        for (c = 0; c < cLen; c++) {
            column = columns[c];

            column.getEditor = column.hasEditor = column.setEditor = null;
        }
    },

    
    
    getColumnField: function(columnHeader, defaultField) {
        var field = columnHeader.field;
        if (!(field && field.isFormField)) {
            field = columnHeader.field = this.createColumnField(columnHeader, defaultField);
        }
        return field;
    },

    
    
    hasColumnField: function(columnHeader) {
        return !!columnHeader.field;
    },

    
    
    setColumnField: function(columnHeader, field) {
        columnHeader.field = field;
        columnHeader.field = this.createColumnField(columnHeader);
    },

    createColumnField:  function(columnHeader, defaultField) {
        var field = columnHeader.field;

        if (!field && columnHeader.editor) {
            field = columnHeader.editor;
            columnHeader.editor = null;
        }

        if (!field && defaultField) {
            field = defaultField;
        }

        if (field) {
            if (field.isComponent) {
                field.column = columnHeader;
                field.isEditorComponent = true;
            } else {
                if (Ext.isString(field)) {
                    field = {
                        name: columnHeader.dataIndex,
                        xtype: field,
                        column: columnHeader,
                        isEditorComponent: true
                    };
                } else {
                    field = Ext.apply({
                        name: columnHeader.dataIndex,
                        column: columnHeader,
                        isEditorComponent: true
                    }, field);
                }
                field = Ext.ComponentManager.create(field, this.defaultFieldXType);
            }
            columnHeader.field = field;
        }
        return field;
    },

    
    initEvents: function() {
        var me = this;
        me.initEditTriggers();
        me.initLockableEvents();
        me.initCancelTriggers();
    },

    
    initCancelTriggers: Ext.emptyFn,

    
    initEditTriggers: function() {
        var me = this,
            view = me.view;

        
        if (me.triggerEvent == 'cellfocus') {
            me.mon(view, 'cellfocus', me.onCellFocus, me);
        } else if (me.triggerEvent == 'rowfocus') {
            me.mon(view, 'rowfocus', me.onRowFocus, me);
        } else {

            
            
            
            
            
            
            if (view.getSelectionModel().isCellModel) {
                view.onCellFocus = Ext.Function.bind(me.beforeViewCellFocus, me);
            }

            
            me.mon(view, me.triggerEvent || ('cell' + (me.clicksToEdit === 1 ? 'click' : 'dblclick')), me.onCellClick, me);
        }

        
        
        me.initAddRemoveHeaderEvents();
        
        view.on('render', me.initKeyNavHeaderEvents, me, {single: true});
    },

    initLockableEvents: function() {
        var me = this,
            grid = me.grid.ownerLockable;

        if (grid) {
            grid.on({
                lockcolumn: me.onColumnLockUnlock,
                unlockcolumn: me.onColumnLockUnlock,
                scope: me
            });
        }
    },

    
    beforeViewCellFocus: function(position) {
        
        if (this.view.selModel.keyNavigation || !this.editing || !this.isCellEditable || !this.isCellEditable(position.row, position.columnHeader)) {
            this.view.focusCell.apply(this.view, arguments);
        }
    },

    
    onRowFocus: function(record, row, rowIdx) {
        this.startEdit(row, 0);
    },

    
    onCellFocus: function(record, cell, position) {
        this.startEdit(position.row, position.column);
    },

    
    
    onCellClick: function(view, cell, colIdx, record, row, rowIdx, e) {
        
        
        
        var expanderSelector = view.expanderSelector,
            
            columnHeader = view.ownerCt.getColumnManager().getHeaderAtIndex(colIdx),
            editor = columnHeader.getEditor(record);

        if (editor && !expanderSelector || !e.getTarget(expanderSelector)) {
            this.startEdit(record, columnHeader);
        }
    },

    initAddRemoveHeaderEvents: function(){
        var me = this;
        me.mon(me.grid.headerCt, {
            scope: me,
            add: me.onColumnAdd,
            remove: me.onColumnRemove,
            columnmove: me.onColumnMove
        });
    },

    initKeyNavHeaderEvents: function() {
        var me = this;

        me.keyNav = Ext.create('Ext.util.KeyNav', me.view.el, {
            enter: me.onEnterKey,
            esc: me.onEscKey,
            scope: me
        });
    },

    
    onColumnAdd: function(ct, column) {
        this.initFieldAccessors(column);
    },

    
    onColumnRemove: function(ct, column) {
        this.removeFieldAccessors(column);
    },

    
    
    
    onColumnMove: function(headerCt, column, fromIdx, toIdx) {
        this.initFieldAccessors(column);
    },

    onColumnLockUnlock: function(view, column) {
        this.initFieldAccessors(column);
    },

    
    onEnterKey: function(e) {
        var me = this,
            grid = me.grid,
            selModel = grid.getSelectionModel(),
            record,
            pos,
            columnHeader;

        
        
        if (selModel.getCurrentPosition && (pos = selModel.getCurrentPosition())) {
            record = pos.record;
            columnHeader = pos.columnHeader;
        }
        
        else {
            record = selModel.getLastSelected();
            columnHeader = grid.getColumnManager().getHeaderAtIndex(0);
        }

        
        if (record && columnHeader) {
            me.startEdit(record, columnHeader);
        }
    },

    
    onEscKey: function(e) {
        return this.cancelEdit();
    },

    
    beforeEdit: Ext.emptyFn,

    
    startEdit: function(record, columnHeader) {
        var me = this,
            context,
            layoutView = me.grid.lockable ? me.grid : me.view;

        
        
        if (!layoutView.componentLayoutCounter) {
            layoutView.on({
                boxready: Ext.Function.bind(me.startEdit, me, [record, columnHeader]),
                single: true
            });
            return false;
        }

        
        if (me.grid.collapsed || !me.grid.view.isVisible(true)) {
            return false;
        }

        context = me.getEditingContext(record, columnHeader);
        if (context == null) {
            return false;
        }
        if (!me.preventBeforeCheck) {
            if (me.beforeEdit(context) === false || me.fireEvent('beforeedit', me, context) === false || context.cancel) {
                return false;
            }
        }

        return context;
    },

    
    
    getEditingContext: function(record, columnHeader) {
        var me = this,
            grid = me.grid,
            colMgr = grid.getColumnManager(),
            view,
            gridRow,
            rowIdx, colIdx;

        
        
        if (Ext.isNumber(columnHeader)) {
            columnHeader = colMgr.getHeaderAtIndex(columnHeader);
        }

        
        if (columnHeader.hidden) {
            columnHeader = columnHeader.next(':not([hidden])') || columnHeader.prev(':not([hidden])');
        }

        
        if (!columnHeader) {
            return;
        }

        
        view = columnHeader.getOwnerHeaderCt().view;

        gridRow = view.getNode(record, true);

        
        if (!gridRow) {
            return;
        }

        colIdx = colMgr.getHeaderIndex(columnHeader);

        if (Ext.isNumber(record)) {
            
            rowIdx = record;
            record = view.getRecord(gridRow);
        } else {
            rowIdx = view.indexOf(gridRow);
        }

        
        
        if (!record) {
            return;
        }

        return {
            grid   : grid,
            view   : view,
            store  : view.dataSource,
            record : record,
            field  : columnHeader.dataIndex,
            value  : record.get(columnHeader.dataIndex),
            row    : gridRow,
            column : columnHeader,
            rowIdx : rowIdx,
            colIdx : colIdx
        };
    },

    
    cancelEdit: function() {
        var me = this;

        me.editing = false;
        me.fireEvent('canceledit', me, me.context);
    },

    
    completeEdit: function() {
        var me = this;

        if (me.editing && me.validateEdit()) {
            me.fireEvent('edit', me, me.context);
        }

        me.context = null;
        me.editing = false;
    },

    
    validateEdit: function() {
        var me = this,
            context = me.context;

        return me.fireEvent('validateedit', me, context) !== false && !context.cancel;
    }
});


Ext.define('Ext.grid.plugin.CellEditing', {
    alias: 'plugin.cellediting',
    extend:  Ext.grid.plugin.Editing ,
                                                              
    lockableScope: 'both',

    
    
    
    

    init: function(grid) {
        var me = this,
            lockingPartner = me.lockingPartner;

        me.callParent(arguments);

        
        if (lockingPartner) {
            if (lockingPartner.editors) {
                me.editors = lockingPartner.editors;
            } else {
                me.editors = lockingPartner.editors = new Ext.util.MixedCollection(false, function(editor) {
                    return editor.editorId;
                });
            }
        } else {
            me.editors = new Ext.util.MixedCollection(false, function(editor) {
                return editor.editorId;
            });
        }
    },

    onReconfigure: function(grid, store, columns){
        
        if (columns) {
            this.editors.clear();
        }
        this.callParent();    
    },

    
    destroy: function() {
        var me = this;
        if (me.editors) {
            me.editors.each(Ext.destroy, Ext);
            me.editors.clear();
        }
        me.callParent(arguments);
    },

    onBodyScroll: function() {
        var me = this,
            ed = me.getActiveEditor(),
            scroll = me.view.el.getScroll();

        
        
        if (ed && ed.editing && ed.editingPlugin === me) {
            
            if (scroll.top !== me.scroll.top) {
                if (ed.field) {
                    if (ed.field.triggerBlur) {
                        ed.field.triggerBlur();
                    } else {
                        ed.field.blur();
                    }
                }
            }
            
            else {
                ed.realign();
            }
        }
        me.scroll = scroll;
    },

    
    
    initCancelTriggers: function() {
        var me   = this,
            grid = me.grid,
            view = grid.view;

        me.mon(view, 'bodyscroll', me.onBodyScroll, me);
        me.mon(grid, {
            columnresize: me.cancelEdit,
            columnmove: me.cancelEdit,
            scope: me
        });
    },

    isCellEditable: function(record, columnHeader) {
        var me = this,
            context = me.getEditingContext(record, columnHeader);

        if (me.grid.view.isVisible(true) && context) {
            columnHeader = context.column;
            record = context.record;
            if (columnHeader && me.getEditor(record, columnHeader)) {
                return true;
            }
        }
    },

    
    startEdit: function(record, columnHeader,  context) {
        var me = this,
            isEditorEditing, isFieldEditable, ed;

        if (!context) {
            me.preventBeforeCheck = true;
            context = me.callParent(arguments);
            delete me.preventBeforeCheck;
            if (context === false) {
                return false;
            }
        }

        
        
        if (context && me.grid.view.isVisible(true)) {

            record = context.record;
            columnHeader = context.column;

            
            
            isFieldEditable = (columnHeader && columnHeader.getEditor(record)) && !(me.beforeEdit(context) === false || me.fireEvent('beforeedit', me, context) === false || context.cancel);

            if (isFieldEditable) {
                ed = me.getEditor(record, columnHeader);
                isEditorEditing = ed.editing;
            }

            
            
            
            me.completeEdit();

            if (!isFieldEditable) {
                return false;
            }

            
            me.context = context;

            context.originalValue = context.value = record.get(columnHeader.dataIndex);

            
            me.grid.view.cancelFocus();
            me.view.scrollCellIntoView(me.getCell(record, columnHeader));
            if (ed) {
                if (Ext.isIE && isEditorEditing) {
                    
                    
                    
                    
                    
                    
                    ed.selectSameEditor = true;
                }
                me.showEditor(ed, context, context.value);
                return true;
            }
            return false;
        }
    },

    showEditor: function(ed, context, value) {
        var me = this,
            record = context.record,
            columnHeader = context.column,
            sm = me.grid.getSelectionModel(),
            preventFocus = sm.preventFocus,
            selection = sm.getCurrentPosition();

        
        
        if (!columnHeader.up(me.view.ownerCt)) {
            return me.lockingPartner.showEditor(ed, me.lockingPartner.getEditingContext(selection.record, selection.columnHeader), value);
        }

        me.setEditingContext(context);
        me.setActiveEditor(ed);
        me.setActiveRecord(record);
        me.setActiveColumn(columnHeader);

        
        
        
        if (!selection || !sm.isCellSelected(me.view, record, columnHeader)) {
            sm.preventFocus = true;
            sm.selectByPosition({
                row: record,
                column: columnHeader,
                view: me.view
            });
            sm.preventFocus = preventFocus;
        }

        
        if (Ext.isIE && Ext.EventObject.type === 'click') {
            Ext.Function.defer(ed.startEdit, 1, ed, [me.getCell(record, columnHeader), value, context]);
        } else {
            ed.startEdit(me.getCell(record, columnHeader), value, context);
        }
        me.editing = true;
        me.scroll = me.view.el.getScroll();
    },

    completeEdit: function() {
        var activeEd = this.getActiveEditor();
        if (activeEd) {
            activeEd.completeEdit();
            this.editing = false;
        }
    },

    
    setEditingContext: function(context) {
        this.context = context;
        if (this.lockingPartner) {
            this.lockingPartner.context = context;
        }
    },

    setActiveEditor: function(ed) {
        this.activeEditor = ed;
        if (this.lockingPartner) {
            this.lockingPartner.activeEditor = ed;
        }
    },

    getActiveEditor: function() {
        return this.activeEditor;
    },

    setActiveColumn: function(column) {
        this.activeColumn = column;
        if (this.lockingPartner) {
            this.lockingPartner.activeColumn = column;
        }
    },

    getActiveColumn: function() {
        return this.activeColumn;
    },

    setActiveRecord: function(record) {
        this.activeRecord = record;
        if (this.lockingPartner) {
            this.lockingPartner.activeRecord = record;
        }
    },

    getActiveRecord: function() {
        return this.activeRecord;
    },

    getEditor: function(record, column) {
        var me = this,
            editors = me.editors,
            editorId = column.getItemId(),
            editor = editors.getByKey(editorId),
            
            editorOwner = me.grid.ownerLockable || me.grid;

        if (!editor) {
            editor = column.getEditor(record);
            if (!editor) {
                return false;
            }

            
            if (editor instanceof Ext.grid.CellEditor) {
                editor.floating = true;
            }
            
            else {
                editor = new Ext.grid.CellEditor({
                    floating: true,
                    editorId: editorId,
                    field: editor
                });
            }
            
            
            
            editor.field.excludeForm = true;
            editorOwner.add(editor);
            editor.on({
                scope: me,
                specialkey: me.onSpecialKey,
                complete: me.onEditComplete,
                canceledit: me.cancelEdit
            });
            column.on('removed', me.cancelActiveEdit, me);
            editors.add(editor);
        }

        if (column.isTreeColumn) {
            editor.isForTree = column.isTreeColumn;
            editor.addCls(Ext.baseCSSPrefix + 'tree-cell-editor');
        }
        editor.grid = me.grid;
        
        
        editor.editingPlugin = me;
        return editor;
    },
    
    cancelActiveEdit: function(column){
        var context = this.context;
        if (context && context.column === column) {
            this.cancelEdit();
        }   
    },
    
    
    setColumnField: function(column, field) {
        var ed = this.editors.getByKey(column.getItemId());
        Ext.destroy(ed, column.field);
        this.editors.removeAtKey(column.getItemId());
        this.callParent(arguments);
    },

    
    getCell: function(record, column) {
        return this.grid.getView().getCell(record, column);
    },

    onSpecialKey: function(ed, field, e) {
        var sm;
 
        if (e.getKey() === e.TAB) {
            e.stopEvent();

            if (ed) {
                
                
                ed.onEditorTab(e);
            }

            sm = ed.up('tablepanel').getSelectionModel();
            if (sm.onEditorTab) {
                return sm.onEditorTab(ed.editingPlugin, e);
            }
        }
    },

    onEditComplete : function(ed, value, startValue) {
        var me = this,
            activeColumn = me.getActiveColumn(),
            context = me.context,
            record;

        if (activeColumn) {
            record = context.record;

            me.setActiveEditor(null);
            me.setActiveColumn(null);
            me.setActiveRecord(null);
    
            context.value = value;
            if (!me.validateEdit()) {
                me.editing = false;
                return;
            }

            
            
            if (!record.isEqual(value, startValue)) {
                record.set(activeColumn.dataIndex, value);
            }

            
            
            context.view.focusRow(context.rowIdx, 100);
            me.fireEvent('edit', me, context);
            me.editing = false;
        }
    },

    
    cancelEdit: function() {
        var me = this,
            context = me.context,
            activeEd = me.getActiveEditor();

        me.setActiveEditor(null);
        me.setActiveColumn(null);
        me.setActiveRecord(null);
        if (activeEd) {
            if (activeEd.field) {
                me.context.value = ('editedValue' in activeEd) ? activeEd.editedValue : activeEd.getValue();
                activeEd.cancelEdit();
            }

            
            
            context.view.focusRow(context.rowIdx, 100);

            
            me.callParent(arguments);
            return;
        }
        
        return true;
    },

    
    startEditByPosition: function(position) {
        var cm = this.grid.getColumnManager(),
            index,
            col;
            
        
        if (!position.isCellContext) {
            position = new Ext.grid.CellContext(this.view).setPosition(position);
        }
        
        
        
        
        index = cm.getHeaderIndex(position.columnHeader);
        position.setColumn(cm.getVisibleHeaderClosestToIndex(index));

        return this.startEdit(position.record, position.columnHeader);
    }
});

Ext.define('Ext.grid.plugin.DivRenderer', {
    alias: 'plugin.divrenderer',
    extend:  Ext.AbstractPlugin ,

    tableTpl: [
        '<div id="{view.id}-table" class="', Ext.baseCSSPrefix, '{view.id}-table ', Ext.baseCSSPrefix, 'grid-table" style="{tableStyle}" {ariaTableAttr}>',
            '{%',
                'values.view.renderRows(values.rows, values.viewStartIndex, out);',
            '%}',
        '</div>',
        {
            priority: 0
        }
    ],

    rowTpl: [
        '{%',
            'var dataRowCls = values.recordIndex === -1 ? "" : " ' + Ext.baseCSSPrefix + 'grid-data-row";',
        '%}',
        '<dl {[values.rowId ? ("id=\\"" + values.rowId + "\\"") : ""]} ',
            'data-boundView="{view.id}" ',
            'data-recordId="{record.internalId}" ',
            'data-recordIndex="{recordIndex}" ',
            'class="{[values.itemClasses.join(" ")]} {[values.rowClasses.join(" ")]}{[dataRowCls]}" ',
            'style="position:relative" ',
            '{rowAttr:attributes} {ariaRowAttr}>',
            '<tpl for="columns">' +
                '{%',
                    'parent.view.renderCell(values, parent.record, parent.recordIndex, xindex - 1, out, parent)',
                 '%}',
            '</tpl>',
        '</dl>',
        {
            priority: 0
        }
    ],

    cellTpl: [
        '<dt class="{tdCls}" {tdAttr} data-cellIndex="{cellIndex}" {ariaCellAttr}>',
            '<div {unselectableAttr} class="' + Ext.baseCSSPrefix + 'grid-cell-inner"',
                'style="text-align:{align};<tpl if="style">{style}</tpl>" {ariaCellInnerAttr}>{value}</div>',
        '</dt>', {
            priority: 0
        }
    ],

    selectors: {
        
        bodySelector: 'div',

        
        nodeContainerSelector: 'div',

        
        itemSelector: 'dl.' + Ext.baseCSSPrefix + 'grid-row',

        
        dataRowSelector: 'dl.' + Ext.baseCSSPrefix + 'grid-data-row',

        
        cellSelector: 'dt.' + Ext.baseCSSPrefix + 'grid-cell',

        innerSelector: 'div.' + Ext.baseCSSPrefix + 'grid-cell-inner',

        getNodeContainerSelector: function() {
            return this.getBodySelector();
        },

        getNodeContainer: function() {
            return this.el.getById(this.id + '-table', true);
        }
    },

    init: function(grid) {
        var view = grid.getView();
        view.tableTpl = Ext.XTemplate.getTpl(this, 'tableTpl');
        view.rowTpl   = Ext.XTemplate.getTpl(this, 'rowTpl');
        view.cellTpl  = Ext.XTemplate.getTpl(this, 'cellTpl');
        Ext.apply(view, this.selectors);
    }
});


Ext.define('Ext.grid.plugin.DragDrop', {
    extend:  Ext.AbstractPlugin ,
    alias: 'plugin.gridviewdragdrop',

           
                            
                               
      

    

    
    

    
    dragText : '{0} selected row{1}',
    

    
    ddGroup : "GridDD",

    

    

    
    enableDrop: true,

    
    enableDrag: true,
    
    
    containerScroll: false,

    init : function(view) {
        view.on('render', this.onViewRender, this, {single: true});
    },

    
    destroy: function() {
        Ext.destroy(this.dragZone, this.dropZone);
    },

    enable: function() {
        var me = this;
        if (me.dragZone) {
            me.dragZone.unlock();
        }
        if (me.dropZone) {
            me.dropZone.unlock();
        }
        me.callParent();
    },

    disable: function() {
        var me = this;
        if (me.dragZone) {
            me.dragZone.lock();
        }
        if (me.dropZone) {
            me.dropZone.lock();
        }
        me.callParent();
    },

    onViewRender : function(view) {
        var me = this,
            scrollEl;

        if (me.enableDrag) {
            if (me.containerScroll) {
                scrollEl = view.getEl();
            }
            
            me.dragZone = new Ext.view.DragZone({
                view: view,
                ddGroup: me.dragGroup || me.ddGroup,
                dragText: me.dragText,
                containerScroll: me.containerScroll,
                scrollEl: scrollEl
            });
        }

        if (me.enableDrop) {
            me.dropZone = new Ext.grid.ViewDropZone({
                view: view,
                ddGroup: me.dropGroup || me.ddGroup
            });
        }
    }
});


Ext.define('Ext.grid.plugin.RowEditing', {
    extend:  Ext.grid.plugin.Editing ,
    alias: 'plugin.rowediting',

               
                            
      

    lockableScope: 'top',

    editStyle: 'row',

    
    autoCancel: true,

    

    
    errorSummary: true,

    constructor: function() {
        var me = this;

        me.callParent(arguments);

        if (!me.clicksToMoveEditor) {
            me.clicksToMoveEditor = me.clicksToEdit;
        }

        me.autoCancel = !!me.autoCancel;
    },

    
    destroy: function() {
        Ext.destroy(this.editor);
        this.callParent(arguments);
    },
    
    onBeforeReconfigure: function() {
        this.callParent(arguments);
        this.cancelEdit();
    },
    
    onReconfigure: function(grid, store, columns) {
        var ed = this.editor;
        this.callParent(arguments);
        
        if (columns && ed && ed.rendered) {
            ed.needsSyncFieldWidths = true;
        }
    },

    
    startEdit: function(record, columnHeader) {
        var me = this,
            editor = me.getEditor(),
            context;
            
        if (Ext.isEmpty(columnHeader)) {
            columnHeader = me.grid.getTopLevelVisibleColumnManager().getHeaderAtIndex(0);
        }

        if (editor.beforeEdit() !== false) {
            context = me.callParent([record, columnHeader]);
            if (context) {
                me.context = context;

                
                if (me.lockingPartner) {
                    me.lockingPartner.cancelEdit();
                }
                editor.startEdit(context.record, context.column, context);
                me.editing = true;
                return true;
            }
        }
        return false;
    },

    
    cancelEdit: function() {
        var me = this;

        if (me.editing) {
            me.getContextFieldValues();
            me.getEditor().cancelEdit();
            me.callParent(arguments);
            return;
        }
        
        return true;
    },

    
    completeEdit: function() {
        var me = this;

        if (me.editing && me.validateEdit()) {
            me.editing = false;
            me.fireEvent('edit', me, me.context);
        }
    },
    
    onEnterKey: function() {
        if (this.getEditor().getForm().isValid()) {
            this.completeEdit();
        }    
    },

    
    validateEdit: function() {
        this.getContextFieldValues();
        this.editing = this.callParent(arguments) && this.getEditor().completeEdit();
        return this.editing;
    },

    
    getEditor: function() {
        var me = this;

        if (!me.editor) {
            me.editor = me.initEditor();
        }
        return me.editor;
    },

    getContextFieldValues: function () {
        var editor         = this.editor,
            context        = this.context,
            record         = context.record,
            newValues      = {},
            originalValues = {},
            editors        = editor.query('>[isFormField]'),
            len            = editors.length,
            i, name, item;

        for (i = 0; i < len; i++) {
            item = editors[i];
            name = item.name;

            newValues[name]      = item.getValue();
            originalValues[name] = record.get(name);
        }

        Ext.apply(context, {
            newValues      : newValues,
            originalValues : originalValues
        });
    },

    
    initEditor: function() {
        return new Ext.grid.RowEditor(this.initEditorConfig());
    },
    
    initEditorConfig: function(){
        var me       = this,
            grid     = me.grid,
            view     = me.view,
            headerCt = grid.headerCt,
            btns     = ['saveBtnText', 'cancelBtnText', 'errorsText', 'dirtyText'],
            b,
            bLen     = btns.length,
            cfg      = {
                autoCancel: me.autoCancel,
                errorSummary: me.errorSummary,
                fields: headerCt.getGridColumns(),
                hidden: true,
                view: view,
                
                editingPlugin: me
            },
            item;

        for (b = 0; b < bLen; b++) {
            item = btns[b];

            if (Ext.isDefined(me[item])) {
                cfg[item] = me[item];
            }
        }
        return cfg;    
    },

    
    initEditTriggers: function() {
        var me = this,
            view = me.view,
            moveEditorEvent = me.clicksToMoveEditor === 1 ? 'click' : 'dblclick';

        me.callParent(arguments);

        if (me.clicksToMoveEditor !== me.clicksToEdit) {
            me.mon(view, 'cell' + moveEditorEvent, me.moveEditorByClick, me);
        }

        view.on({
            render: function() {
                me.mon(me.grid.headerCt, {
                    scope: me,
                    columnresize: me.onColumnResize,
                    columnhide: me.onColumnHide,
                    columnshow: me.onColumnShow
                });
            },
            single: true
        });
    },

    startEditByClick: function() {
        var me = this;
        if (!me.editing || me.clicksToMoveEditor === me.clicksToEdit) {
            me.callParent(arguments);
        }
    },

    moveEditorByClick: function() {
        var me = this;
        if (me.editing) {
            me.superclass.onCellClick.apply(me, arguments);
        }
    },
    
    
    onColumnAdd: function(ct, column) {
        if (column.isHeader) {
            var me = this,
                editor;

            me.initFieldAccessors(column);

            
            
            editor = me.editor;
            if (editor) {
                editor.onColumnAdd(column);
            }
        }
    },

    
    onColumnRemove: function(ct, column) {
        if (column.isHeader) {
            var me = this,
                editor = me.getEditor();

            if (editor) {
                editor.onColumnRemove(ct, column);
            }
            me.removeFieldAccessors(column);
        }
    },

    
    onColumnResize: function(ct, column, width) {
        if (column.isHeader) {
            var me = this,
                editor = me.getEditor();

            if (editor) {
                editor.onColumnResize(column, width);
            }
        }
    },

    
    onColumnHide: function(ct, column) {
        
        var me = this,
            editor = me.getEditor();

        if (editor) {
            editor.onColumnHide(column);
        }
    },

    
    onColumnShow: function(ct, column) {
        
        var me = this,
            editor = me.getEditor();

        if (editor) {
            editor.onColumnShow(column);
        }
    },

    
    onColumnMove: function(ct, column, fromIdx, toIdx) {
        
        var me = this,
            editor = me.getEditor();

        
        
        me.initFieldAccessors(column);

        if (editor) {
            
            
            editor.onColumnMove(column, fromIdx, toIdx);
        }
    },

    
    setColumnField: function(column, field) {
        var me = this,
            editor = me.getEditor();

        if (editor) {
            
            editor.removeColumnEditor(column, true);
        }

        me.callParent(arguments);

        if (editor) {
            editor.insertColumnEditor(column);
        }
    },

    createColumnField: function(column, defaultField) {
        var editor = this.editor,
            def;

        if (editor) {
            def = editor.getDefaultFieldCfg();
        }

        return this.callParent([column, defaultField || def]);
    }
});






Ext.define('Ext.grid.plugin.RowExpander', {
    extend:  Ext.AbstractPlugin ,
    lockableScope: 'normal',

               
                                   
                                  
      

    alias: 'plugin.rowexpander',

    rowBodyTpl: null,

    
    expandOnEnter: true,

    
    expandOnDblClick: true,

    
    selectRowOnExpand: false,

    rowBodyTrSelector: '.' + Ext.baseCSSPrefix + 'grid-rowbody-tr',
    rowBodyHiddenCls: Ext.baseCSSPrefix + 'grid-row-body-hidden',
    rowCollapsedCls: Ext.baseCSSPrefix + 'grid-row-collapsed',

    addCollapsedCls: {
        before: function(values, out) {
            var me = this.rowExpander;
            if (!me.recordsExpanded[values.record.internalId]) {
                values.itemClasses.push(me.rowCollapsedCls);
            }
        },
        priority: 500
    },

    
    

    setCmp: function(grid) {
        var me = this,
            rowBodyTpl,
            features;

        me.callParent(arguments);

        me.recordsExpanded = {};

        me.rowBodyTpl = Ext.XTemplate.getTpl(me, 'rowBodyTpl');
        rowBodyTpl = this.rowBodyTpl;
        features = [{
            ftype: 'rowbody',
            lockableScope: 'normal',
            recordsExpanded: me.recordsExpanded,
            rowBodyHiddenCls: me.rowBodyHiddenCls,
            rowCollapsedCls: me.rowCollapsedCls,
            setupRowData: me.getRowBodyFeatureData,
            setup: me.setup,
            getRowBodyContents: function(record) {
                return rowBodyTpl.applyTemplate(record.getData());
            }
        },{
            ftype: 'rowwrap',
            lockableScope: 'normal'
        }];
 
        if (grid.features) {
            grid.features = Ext.Array.push(features, grid.features);
        } else {
            grid.features = features;
        }
        
    },

    init: function(grid) {
        var me = this,
            reconfigurable = grid,
            view, normalView, lockedView;

        if (grid.lockable) {
            grid = grid.lockedGrid;
        }

        me.callParent(arguments);
        me.grid = grid;
        view = me.view = grid.getView();
        
        
        me.addExpander();
        
        
        
        me.bindView(view);
        view.addRowTpl(me.addCollapsedCls).rowExpander = me;

        
        
        if (grid.ownerLockable) {
            
            reconfigurable = grid.ownerLockable;
            reconfigurable.syncRowHeight = false;
            lockedView = reconfigurable.lockedGrid.getView();

            
            
            me.bindView(lockedView);
            lockedView.addRowTpl(me.addCollapsedCls).rowExpander = me;

            
            
            reconfigurable.mon(reconfigurable, 'columnschanged', me.refreshRowHeights, me);
            reconfigurable.mon(reconfigurable.store, 'datachanged', me.refreshRowHeights, me);
        }
        reconfigurable.on('beforereconfigure', me.beforeReconfigure, me);

        if (grid.ownerLockable && !grid.rowLines) {
            
            
            
            
            view.on('rowfocus', me.refreshRowHeights, me);
        }
    },
    
    beforeReconfigure: function(grid, store, columns, oldStore, oldColumns) {
        var expander = this.getHeaderConfig();
        expander.locked = true;
        columns.unshift(expander);
    },

    
    addExpander: function() {
        var me = this,
            expanderGrid = me.grid,
            expanderHeader = me.getHeaderConfig();

        
        if (expanderGrid.ownerLockable) {
            expanderGrid = expanderGrid.ownerLockable.lockedGrid;
            expanderGrid.width += expanderHeader.width;
        }
        expanderGrid.headerCt.insert(0, expanderHeader);

        
        
        expanderGrid.getSelectionModel().injectCheckbox = 1;
    },

    getRowBodyFeatureData: function(record, idx, rowValues) {
        var me = this;
        me.self.prototype.setupRowData.apply(me, arguments);

        rowValues.rowBody = me.getRowBodyContents(record);
        rowValues.rowBodyCls = me.recordsExpanded[record.internalId] ? '' : me.rowBodyHiddenCls;
    },
    
    setup: function(rows, rowValues){
        var me = this;
        me.self.prototype.setup.apply(me, arguments);
        
        if (!me.grid.ownerLockable) {
            rowValues.rowBodyColspan -= 1;
        }    
    },

    bindView: function(view) {
        if (this.expandOnEnter) {
            view.on('itemkeydown', this.onKeyDown, this);
        }
        if (this.expandOnDblClick) {
            view.on('itemdblclick', this.onDblClick, this);
        }
    },

    onKeyDown: function(view, record, row, rowIdx, e) {
        if (e.getKey() == e.ENTER) {
            var ds   = view.store,
                sels = view.getSelectionModel().getSelection(),
                ln   = sels.length,
                i = 0;

            for (; i < ln; i++) {
                rowIdx = ds.indexOf(sels[i]);
                this.toggleRow(rowIdx, sels[i]);
            }
        }
    },

    onDblClick: function(view, record, row, rowIdx, e) {
        this.toggleRow(rowIdx, record);
    },

    toggleRow: function(rowIdx, record) {
        var me = this,
            view = me.view,
            rowNode = view.getNode(rowIdx),
            row = Ext.fly(rowNode, '_rowExpander'),
            nextBd = row.down(me.rowBodyTrSelector, true),
            isCollapsed = row.hasCls(me.rowCollapsedCls),
            addOrRemoveCls = isCollapsed ? 'removeCls' : 'addCls',
            ownerLock, rowHeight, fireView;

        
        Ext.suspendLayouts();
        row[addOrRemoveCls](me.rowCollapsedCls);
        Ext.fly(nextBd)[addOrRemoveCls](me.rowBodyHiddenCls);
        me.recordsExpanded[record.internalId] = isCollapsed;
        view.refreshSize();

        
        if (me.grid.ownerLockable) {
            ownerLock = me.grid.ownerLockable;
            fireView = ownerLock.getView();
            view = ownerLock.lockedGrid.view;
            rowHeight = row.getHeight();
            
            
            
            
            row.setHeight(isCollapsed ? rowHeight : '');
            row = Ext.fly(view.getNode(rowIdx), '_rowExpander');
            row.setHeight(isCollapsed ? rowHeight : '');
            row[addOrRemoveCls](me.rowCollapsedCls);
            view.refreshSize();
        } else {
            fireView = view;
        }
        fireView.fireEvent(isCollapsed ? 'expandbody' : 'collapsebody', row.dom, record, nextBd);
        
        Ext.resumeLayouts(true);
    },

    
    
    
    
    
    
    refreshRowHeights: function() {
        Ext.globalEvents.on({
            idle: this.doRefreshRowHeights,
            scope: this,
            single: true
        });
    },

    doRefreshRowHeights: function() {
        var me = this,
            recordsExpanded = me.recordsExpanded,
            key, record,
            lockedView = me.grid.ownerLockable.lockedGrid.view,
            normalView = me.grid.ownerLockable.normalGrid.view,
            normalRow,
            lockedRow,
            lockedHeight,
            normalHeight;

        for (key in recordsExpanded) {
            if (recordsExpanded.hasOwnProperty(key)) {
                record = this.view.store.data.get(key);
                lockedRow = lockedView.getNode(record, false);
                normalRow = normalView.getNode(record, false);
                lockedRow.style.height = normalRow.style.height = '';
                lockedHeight = lockedRow.offsetHeight;
                normalHeight = normalRow.offsetHeight;
                if (normalHeight > lockedHeight) {
                    lockedRow.style.height = normalHeight + 'px';
                } else if (lockedHeight > normalHeight) {
                    normalRow.style.height = lockedHeight + 'px';
                }
            }
        }
    },

    getHeaderConfig: function() {
        var me = this;

        return {
            width: 24,
            lockable: false,
            sortable: false,
            resizable: false,
            draggable: false,
            hideable: false,
            menuDisabled: true,
            tdCls: Ext.baseCSSPrefix + 'grid-cell-special',
            innerCls: Ext.baseCSSPrefix + 'grid-cell-inner-row-expander',
            renderer: function(value, metadata) {
                
                if (!me.grid.ownerLockable) {
                    metadata.tdAttr += ' rowspan="2"';
                }
                return '<div class="' + Ext.baseCSSPrefix + 'grid-row-expander" role="presentation"></div>';
            },
            processEvent: function(type, view, cell, rowIndex, cellIndex, e, record) {
                if (type == "mousedown" && e.getTarget('.' + Ext.baseCSSPrefix + 'grid-row-expander')) {
                    me.toggleRow(rowIndex, record);
                    return me.selectRowOnExpand;
                }
            }
        };
    }
});


Ext.define('Ext.grid.property.Grid', {

    extend:  Ext.grid.Panel ,

    alias: 'widget.propertygrid',

    alternateClassName: 'Ext.grid.PropertyGrid',

           
                                     
                                 
                                           
                       
                             
                             
                             
                               
                                
      

    

   

    

    

    

    
    valueField: 'value',

    
    nameField: 'name',

    
    inferTypes: true,

    

    
    enableColumnMove: false,
    columnLines: true,
    stripeRows: false,
    trackMouseOver: false,
    clicksToEdit: 1,
    enableHdMenu: false,

    gridCls: Ext.baseCSSPrefix + 'property-grid',

    
    initComponent : function() {
        var me = this;

        me.source = me.source || {};
        me.addCls(me.gridCls);
        me.plugins = me.plugins || [];

        
        me.plugins.push(new Ext.grid.plugin.CellEditing({
            clicksToEdit: me.clicksToEdit,

            
            startEdit: function(record, column) {
                
                return this.self.prototype.startEdit.call(this, record, me.valueColumn);
            }
        }));

        me.selModel = {
            selType: 'cellmodel',
            onCellSelect: function(position) {
                
                position.columnHeader = me.valueColumn;
                position.column = me.valueColumn.getVisibleIndex();
                return this.self.prototype.onCellSelect.call(this, position);
            }
        };

        me.sourceConfig = Ext.apply({}, me.sourceConfig);

        
        if (!me.store) {
            me.propStore = me.store = new Ext.grid.property.Store(me, me.source);
        }
        me.configure(me.sourceConfig);

        if (me.sortableColumns) {
            me.store.sort('name', 'ASC');
        }
        me.columns = new Ext.grid.property.HeaderContainer(me, me.store);

        me.addEvents(
            
            'beforepropertychange',
            
            'propertychange'
        );
        me.callParent();

        
        me.getView().walkCells = this.walkCells;

        
        me.editors = {
            'date'    : new Ext.grid.CellEditor({ field: new Ext.form.field.Date({selectOnFocus: true})}),
            'string'  : new Ext.grid.CellEditor({ field: new Ext.form.field.Text({selectOnFocus: true})}),
            'number'  : new Ext.grid.CellEditor({ field: new Ext.form.field.Number({selectOnFocus: true})}),
            'boolean' : new Ext.grid.CellEditor({ field: new Ext.form.field.ComboBox({
                editable: false,
                store: [[ true, me.headerCt.trueText ], [false, me.headerCt.falseText ]]
            })})
        };

        
        me.store.on('update', me.onUpdate, me);
    },

    configure: function(config){
        var me = this,
            store = me.store,
            i = 0,
            len = me.store.getCount(),
            nameField = me.nameField,
            valueField = me.valueField,
            name, value, rec, type;

        me.configureLegacy(config);

        if (me.inferTypes) {
            for (; i < len; ++i) {
                rec = store.getAt(i);
                name = rec.get(nameField);
                if (!me.getConfig(name, 'type')) {
                    value = rec.get(valueField);
                    if (Ext.isDate(value)) {
                        type = 'date';
                    } else if (Ext.isNumber(value)) {
                        type = 'number';
                    } else if (Ext.isBoolean(value)) {
                        type = 'boolean';
                    } else {
                        type = 'string';
                    }
                    me.setConfig(name, 'type', type);
                }
            }
        }
    },

    getConfig: function(fieldName, key, defaultValue) {
        var config = this.sourceConfig[fieldName],
            out;

        if (config) {
            out = config[key];
        }
        return out || defaultValue;
    },

    setConfig: function(fieldName, key, value) {
        var sourceCfg = this.sourceConfig,
            o = sourceCfg[fieldName];

        if (!o) {
            o = sourceCfg[fieldName] = {
                __copied: true
            };
        } else if (!o.__copied) {
            o = Ext.apply({
                __copied: true
            }, o);
            sourceCfg[fieldName] = o;
        }
        o[key] = value;
        return value;
    },

    
    configureLegacy: function(config){
        var me = this;

        me.copyLegacyObject(config, me.customRenderers, 'renderer');
        me.copyLegacyObject(config, me.customEditors, 'editor');
        me.copyLegacyObject(config, me.propertyNames, 'displayName');

    },

    copyLegacyObject: function(config, o, keyName){
        var key;

        for (key in o) {
            if (o.hasOwnProperty(key)) {
                if (!config[key]) {
                    config[key] = {};
                }
                config[key][keyName] = o[key];
            }
        }
    },

    
    onUpdate : function(store, record, operation) {
        var me = this,
            v, oldValue;

        if (me.rendered && operation == Ext.data.Model.EDIT) {
            v = record.get(me.valueField);
            oldValue = record.modified.value;
            if (me.fireEvent('beforepropertychange', me.source, record.getId(), v, oldValue) !== false) {
                if (me.source) {
                    me.source[record.getId()] = v;
                }
                record.commit();
                me.fireEvent('propertychange', me.source, record.getId(), v, oldValue);
            } else {
                record.reject();
            }
        }
    },

    
    
    walkCells: function(pos, direction, e, preventWrap, verifierFn, scope) {
        var me = this,
            valueColumn = me.ownerCt.valueColumn;

        if (direction == 'left') {
            direction = 'up';
        } else if (direction == 'right') {
            direction = 'down';
        }
        pos = Ext.view.Table.prototype.walkCells.call(me, pos, direction, e, preventWrap, verifierFn, scope);

        
        pos.columnHeader = valueColumn;
        pos.column = valueColumn.getVisibleIndex();
        return pos;
    },

    
    
    getCellEditor : function(record, column) {
        var me = this,
            propName = record.get(me.nameField),
            val = record.get(me.valueField),
            editor = me.getConfig(propName, 'editor'),
            type = me.getConfig(propName, 'type'),
            editors = me.editors;

        
        
        if (editor) {
            if (!(editor instanceof Ext.grid.CellEditor)) {
                if (!(editor instanceof Ext.form.field.Base)) {
                    editor = Ext.ComponentManager.create(editor, 'textfield');
                }
                editor = me.setConfig(propName, 'editor', new Ext.grid.CellEditor({ field: editor }));
            }
        } else if (type) {
            switch (type) {
                case 'date':
                    editor = editors.date;
                    break;
                case 'number':
                    editor = editors.number;
                    break;
                case 'boolean':
                    editor = me.editors['boolean'];
                    break;
                default:
                    editor = editors.string;
            }
        } else if (Ext.isDate(val)) {
            editor = editors.date;
        } else if (Ext.isNumber(val)) {
            editor = editors.number;
        } else if (Ext.isBoolean(val)) {
            editor = editors['boolean'];
        } else {
            editor = editors.string;
        }

        
        editor.editorId = propName;
        return editor;
    },

    beforeDestroy: function() {
        var me = this;
        me.callParent();
        me.destroyEditors(me.editors);
        me.destroyEditors(me.customEditors);
        delete me.source;
    },

    destroyEditors: function (editors) {
        for (var ed in editors) {
            if (editors.hasOwnProperty(ed)) {
                Ext.destroy(editors[ed]);
            }
        }
    },

    
    setSource: function(source, sourceConfig) {
        var me = this;

        me.source = source;
        if (sourceConfig !== undefined) {
            me.sourceConfig = Ext.apply({}, sourceConfig);
            me.configure(me.sourceConfig);
        }
        me.propStore.setSource(source);
    },

    
    getSource: function() {
        return this.propStore.getSource();
    },

    
    setProperty: function(prop, value, create) {
        this.propStore.setValue(prop, value, create);
    },

    
    removeProperty: function(prop) {
        this.propStore.remove(prop);
    }

    
    
});


Ext.define('Ext.grid.property.HeaderContainer', {

    extend:  Ext.grid.header.Container ,

    alternateClassName: 'Ext.grid.PropertyColumnModel',

    nameWidth: 115,

    
    
    nameText : 'Name',
    
    
    valueText : 'Value',
    
    
    dateFormat : 'm/j/Y',
    
    
    trueText: 'true',
    
    
    falseText: 'false',
    

    
    nameColumnCls: Ext.baseCSSPrefix + 'grid-property-name',
    nameColumnInnerCls: Ext.baseCSSPrefix + 'grid-cell-inner-property-name',

    
    constructor : function(grid, store) {
        var me = this;

        me.grid = grid;
        me.store = store;
        me.callParent([{
            isRootHeader: true,
            enableColumnResize: Ext.isDefined(grid.enableColumnResize) ? grid.enableColumnResize : me.enableColumnResize,
            enableColumnMove: Ext.isDefined(grid.enableColumnMove) ? grid.enableColumnMove : me.enableColumnMove,
            items: [{
                header: me.nameText,
                width: grid.nameColumnWidth || me.nameWidth,
                sortable: grid.sortableColumns,
                dataIndex: grid.nameField,
                renderer: Ext.Function.bind(me.renderProp, me),
                itemId: grid.nameField,
                menuDisabled: true,
                tdCls: me.nameColumnCls,
                innerCls: me.nameColumnInnerCls
            }, {
                header: me.valueText,
                renderer: Ext.Function.bind(me.renderCell, me),
                getEditor: Ext.Function.bind(me.getCellEditor, me),
                sortable: grid.sortableColumns,
                flex: 1,
                fixed: true,
                dataIndex: grid.valueField,
                itemId: grid.valueField,
                menuDisabled: true
            }]
        }]);

        
        me.grid.valueColumn = me.items.items[1];
    },

    getCellEditor: function(record){
        return this.grid.getCellEditor(record, this);
    },

    
    
    renderProp : function(v) {
        return this.getPropertyName(v);
    },

    
    
    renderCell : function(val, meta, rec) {
        var me = this,
            grid = me.grid,
            renderer = grid.getConfig(rec.get(grid.nameField), 'renderer'),
            result = val;

        if (renderer) {
            return renderer.apply(me, arguments);
        }
        if (Ext.isDate(val)) {
            result = me.renderDate(val);
        } else if (Ext.isBoolean(val)) {
            result = me.renderBool(val);
        }
        return Ext.util.Format.htmlEncode(result);
    },

    
    renderDate : Ext.util.Format.date,

    
    renderBool : function(bVal) {
        return this[bVal ? 'trueText' : 'falseText'];
    },

    
    
    getPropertyName : function(name) {
        return this.grid.getConfig(name, 'displayName', name);
    }
});


Ext.define('Ext.grid.property.Property', {
    extend:  Ext.data.Model ,

    alternateClassName: 'Ext.PropGridProperty',

    fields: [{
        name: 'name',
        type: 'string'
    }, {
        name: 'value'
    }],
    idProperty: 'name'
});


Ext.define('Ext.grid.property.Store', {

    extend:  Ext.data.Store ,

    alternateClassName: 'Ext.grid.PropertyStore',

    sortOnLoad: false,

                                                                                                                 

    
    constructor : function(grid, source){
        var me = this;
        
        me.grid = grid;
        me.source = source;
        me.callParent([{
            data: source,
            model: Ext.grid.property.Property,
            proxy: me.getProxy()
        }]);
    },

    
    getProxy: function() {
        if (!this.proxy) {
            Ext.grid.property.Store.prototype.proxy = new Ext.data.proxy.Memory({
                model: Ext.grid.property.Property,
                reader: this.getReader()
            });
        }
        return this.proxy;
    },

    
    getReader: function() {
        if (!this.reader) {
            Ext.grid.property.Store.prototype.reader = new Ext.data.reader.Reader({
                model: Ext.grid.property.Property,

                buildExtractors: Ext.emptyFn,

                read: function(dataObject) {
                    return this.readRecords(dataObject);
                },

                readRecords: function(dataObject) {
                    var val,
                        propName,
                        result = {
                            records: [],
                            success: true
                        };

                    for (propName in dataObject) {
                        if (dataObject.hasOwnProperty(propName)) {
                            val = dataObject[propName];
                            if (this.isEditableValue(val)) {
                                result.records.push(new Ext.grid.property.Property({
                                    name: propName,
                                    value: val
                                }, propName));
                            }
                        }
                    }
                    result.total = result.count = result.records.length;
                    return new Ext.data.ResultSet(result);
                },

                
                isEditableValue: function(val){
                    return Ext.isPrimitive(val) || Ext.isDate(val) || val === null;
                }
            });
        }
        return this.reader;
    },

    
    
    setSource : function(dataObject) {
        var me = this;

        me.source = dataObject;
        me.suspendEvents();
        me.removeAll();
        me.proxy.data = dataObject;
        me.load();
        me.resumeEvents();
        me.fireEvent('datachanged', me);
        me.fireEvent('refresh', me);
    },

    
    getProperty : function(row) {
       return Ext.isNumber(row) ? this.getAt(row) : this.getById(row);
    },

    
    setValue : function(prop, value, create){
        var me = this,
            rec = me.getRec(prop);
            
        if (rec) {
            rec.set('value', value);
            me.source[prop] = value;
        } else if (create) {
            
            me.source[prop] = value;
            rec = new Ext.grid.property.Property({name: prop, value: value}, prop);
            me.add(rec);
        }
    },

    
    remove : function(prop) {
        var rec = this.getRec(prop);
        if (rec) {
            this.callParent([rec]);
            delete this.source[prop];
        }
    },

    
    getRec : function(prop) {
        return this.getById(prop);
    },

    
    
    getSource : function() {
        return this.source;
    }
});


Ext.define('Ext.layout.ClassList', (function () {

    var splitWords = Ext.String.splitWords,
        toMap = Ext.Array.toMap;

    return {
        dirty: false,

        constructor: function (owner) {
            this.owner = owner;
            this.map = toMap(this.classes = splitWords(owner.el.className));
        },

        
        add: function (cls) {
            var me = this;

            if (!me.map[cls]) {
                me.map[cls] = true;
                me.classes.push(cls);
                if (!me.dirty) {
                    me.dirty = true;
                    me.owner.markDirty();
                }
            }
        },

        
        addMany: function (classes) {
            Ext.each(splitWords(classes), this.add, this);
        },

        contains: function (cls) {
            return this.map[cls];
        },

        flush: function () {
            this.owner.el.className = this.classes.join(' ');
            this.dirty = false;
        },

        
        remove: function (cls) {
            var me = this;

            if (me.map[cls]) {
                delete me.map[cls];
                me.classes = Ext.Array.filter(me.classes, function (c) {
                    return c != cls;
                });
                if (!me.dirty) {
                    me.dirty = true;
                    me.owner.markDirty();
                }
            }
        },

        
        removeMany: function (classes) {
            var me = this,
                remove = toMap(splitWords(classes));

            me.classes = Ext.Array.filter(me.classes, function (c) {
                if (!remove[c]) {
                    return true;
                }

                delete me.map[c];
                if (!me.dirty) {
                    me.dirty = true;
                    me.owner.markDirty();
                }
                return false;
            });
        }
    };
}()));


Ext.define('Ext.util.Queue', {

    constructor: function() {
        this.clear();
    },

    add : function(obj) {
        var me = this,
            key = me.getKey(obj);

        if (!me.map[key]) {
            ++me.length;
            me.items.push(obj);
            me.map[key] = obj;
        }

        return obj;
    },

    
    clear : function(){
        var me = this,
            items = me.items;

        me.items = [];
        me.map = {};
        me.length = 0;

        return items;
    },

    contains: function (obj) {
        var key = this.getKey(obj);

        return this.map.hasOwnProperty(key);
    },

    
    getCount : function(){
        return this.length;
    },

    getKey : function(obj){
         return obj.id;
    },

    
    remove : function(obj){
        var me = this,
            key = me.getKey(obj),
            items = me.items,
            index;

        if (me.map[key]) {
            index = Ext.Array.indexOf(items, obj);
            Ext.Array.erase(items, index, 1);
            delete me.map[key];
            --me.length;
        }

        return obj;
    }
});


Ext.define('Ext.layout.ContextItem', {

                                       

    heightModel: null,
    widthModel: null,
    sizeModel: null,

    
    optOut: false,

    ownerSizePolicy: null, 

    boxChildren: null,

    boxParent: null,
    isBorderBoxValue: null,

    children: [],

    dirty: null,

    
    dirtyCount: 0,

    hasRawContent: true,

    isContextItem: true,

    isTopLevel: false,

    consumersContentHeight: 0,
    consumersContentWidth: 0,
    consumersContainerHeight: 0,
    consumersContainerWidth: 0,
    consumersHeight: 0,
    consumersWidth: 0,

    ownerCtContext: null,

    remainingChildDimensions: 0,

    
    props: null,

    
    state: null,

    
    wrapsComponent: false,

    constructor: function (config) {
        var me = this,
            sizeModels = Ext.layout.SizeModel.sizeModels,
            configured = sizeModels.configured,
            shrinkWrap = sizeModels.shrinkWrap,
            el, lastBox, ownerCt, ownerCtContext, props, sizeModel, target,
            lastWidth, lastHeight, sameWidth, sameHeight, widthModel, heightModel, optOut;

        Ext.apply(me, config);

        el = me.el;
        me.id = el.id;

        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        

        me.flushedProps = {};
        me.props = props = {};

        
        me.styles = {};

        target = me.target;

        if (!target.isComponent) {
            lastBox = el.lastBox;
        } else {
            me.wrapsComponent = true;
            me.framing = target.frameSize || null;
            me.isComponentChild = target.ownerLayout && target.ownerLayout.isComponentLayout;

            lastBox = target.lastBox;

            
            
            ownerCt = target.ownerCt;
            if (ownerCt && (ownerCtContext = ownerCt.el && me.context.items[ownerCt.el.id])) {
                me.ownerCtContext = ownerCtContext;
            }

            
            
            me.sizeModel = sizeModel = target.getSizeModel(ownerCtContext &&
                ownerCtContext.widthModel.pairsByHeightOrdinal[ownerCtContext.heightModel.ordinal]);

            
            
            
            
            

            me.widthModel = widthModel = sizeModel.width;
            me.heightModel = heightModel = sizeModel.height;

            
            
            
            if (lastBox && lastBox.invalid === false) {
                sameWidth = (target.width === (lastWidth = lastBox.width));
                sameHeight = (target.height === (lastHeight = lastBox.height));

                if (widthModel === shrinkWrap && heightModel === shrinkWrap) {
                    optOut = true;
                } else if (widthModel === configured && sameWidth) {
                    optOut = heightModel === shrinkWrap ||
                            (heightModel === configured && sameHeight);
                }

                if (optOut) {
                    
                    me.optOut = true;
                    props.width = lastWidth;
                    props.height = lastHeight;
                }
            }
        }

        me.lastBox = lastBox;
    },

    
    init: function (full, options) {
        var me = this,
            oldProps = me.props,
            oldDirty = me.dirty,
            ownerCtContext = me.ownerCtContext,
            ownerLayout = me.target.ownerLayout,
            firstTime = !me.state,
            ret = full || firstTime,
            children, i, n, ownerCt, sizeModel, target,
            oldHeightModel = me.heightModel,
            oldWidthModel = me.widthModel,
            newHeightModel, newWidthModel,
            remainingCount = 0;

        me.dirty = me.invalid = false;
        me.props = {};

        
        me.remainingChildDimensions = 0;

        if (me.boxChildren) {
            me.boxChildren.length = 0; 
        }

        if (!firstTime) {
            me.clearAllBlocks('blocks');
            me.clearAllBlocks('domBlocks');
        }

        
        if (!me.wrapsComponent) {
            return ret;
        }

        
        target = me.target;
        me.state = {}; 

        if (firstTime) {
            
            
            if (target.beforeLayout && target.beforeLayout !== Ext.emptyFn) {
                target.beforeLayout();
            }

            
            
            
            
            
            
            if (!ownerCtContext && (ownerCt = target.ownerCt)) {
                ownerCtContext = me.context.items[ownerCt.el.id];
            }

            if (ownerCtContext) {
                me.ownerCtContext = ownerCtContext;
                me.isBoxParent = target.ownerLayout.isItemBoxParent(me);
            } else {
                me.isTopLevel = true; 
            }

            me.frameBodyContext = me.getEl('frameBody');
        } else {
            ownerCtContext = me.ownerCtContext;

            
            me.isTopLevel = !ownerCtContext;

            
            
            children = me.children;
            for (i = 0, n = children.length; i < n; ++i) {
                children[i].init(true);
            }
        }

        
        
        
        me.hasRawContent = !(target.isContainer && target.items.items.length > 0);

        if (full) {
            
            
            me.widthModel = me.heightModel = null;
            sizeModel = target.getSizeModel(ownerCtContext && 
                ownerCtContext.widthModel.pairsByHeightOrdinal[ownerCtContext.heightModel.ordinal]);

            if (firstTime) {
                me.sizeModel = sizeModel;
            }

            me.widthModel = sizeModel.width;
            me.heightModel = sizeModel.height;

            
            
            
            if (ownerCtContext && !me.isComponentChild) {
                ownerCtContext.remainingChildDimensions += 2;
            }
        } else if (oldProps) {
            
            
            me.recoverProp('x', oldProps, oldDirty);
            me.recoverProp('y', oldProps, oldDirty);
            
            
            if (me.widthModel.calculated) {
                me.recoverProp('width', oldProps, oldDirty);
            } else if ('width' in oldProps) {
                ++remainingCount;
            }
            if (me.heightModel.calculated) {
                me.recoverProp('height', oldProps, oldDirty);
            } else if ('height' in oldProps) {
                ++remainingCount;
            }
            
            
            
            
            
            
            if (ownerCtContext && !me.isComponentChild) {
                ownerCtContext.remainingChildDimensions += remainingCount;
            }
        }

        if (oldProps && ownerLayout && ownerLayout.manageMargins) {
            me.recoverProp('margin-top', oldProps, oldDirty);
            me.recoverProp('margin-right', oldProps, oldDirty);
            me.recoverProp('margin-bottom', oldProps, oldDirty);
            me.recoverProp('margin-left', oldProps, oldDirty);
        }

        
        
        if (options) {
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            newHeightModel = options.heightModel;
            newWidthModel = options.widthModel;
            if (newWidthModel && newHeightModel && oldWidthModel && oldHeightModel) {
                if (oldWidthModel.shrinkWrap && oldHeightModel.shrinkWrap) {
                    if (newWidthModel.constrainedMax && newHeightModel.constrainedMin) {
                        newHeightModel = null;
                    }
                }
            }

            
            if (newWidthModel) {
                me.widthModel = newWidthModel;
            }
            if (newHeightModel) {
                me.heightModel = newHeightModel;
            }

            if (options.state) {
                Ext.apply(me.state, options.state);
            }
        }

        return ret;
    },

    
    initContinue: function (full) {
        var me = this,
            ownerCtContext = me.ownerCtContext,
            comp = me.target,
            widthModel = me.widthModel,
            hierarchyState = comp.getHierarchyState(),
            boxParent;

        if (widthModel.fixed) { 
            hierarchyState.inShrinkWrapTable = false;
        } else {
            delete hierarchyState.inShrinkWrapTable;
        }

        if (full) {
            if (ownerCtContext && widthModel.shrinkWrap) {
                boxParent = ownerCtContext.isBoxParent ? ownerCtContext : ownerCtContext.boxParent;
                if (boxParent) {
                    boxParent.addBoxChild(me);
                }
            } else if (widthModel.natural) {
                me.boxParent = ownerCtContext;
            }
        }

        return full;
    },

    
    initDone: function(containerLayoutDone) {
        var me = this,
            props = me.props,
            state = me.state;

        
        if (me.remainingChildDimensions === 0) {
            props.containerChildrenSizeDone = true;
        }
        if (containerLayoutDone) {
            props.containerLayoutDone = true;
        }

        if (me.boxChildren && me.boxChildren.length && me.widthModel.shrinkWrap) {
            
            
            me.el.setWidth(10000);

            
            state.blocks = (state.blocks || 0) + 1;
        }
    },

    
    initAnimation: function() {
        var me = this,
            target = me.target,
            ownerCtContext = me.ownerCtContext;

        if (ownerCtContext && ownerCtContext.isTopLevel) {
            
            
            me.animatePolicy = target.ownerLayout.getAnimatePolicy(me);
        } else if (!ownerCtContext && target.isCollapsingOrExpanding && target.animCollapse) {
            
            
            
            me.animatePolicy = target.componentLayout.getAnimatePolicy(me);
        }

        if (me.animatePolicy) {
            me.context.queueAnimation(me);
        }
    },

    
    addCls: function(newCls) {
        this.getClassList().addMany(newCls);
    },

    
    removeCls: function(removeCls) {
        this.getClassList().removeMany(removeCls);
    },

    
    addBlock: function (name, layout, propName) {
        var me = this,
            collection = me[name] || (me[name] = {}),
            blockedLayouts = collection[propName] || (collection[propName] = {});

        if (!blockedLayouts[layout.id]) {
            blockedLayouts[layout.id] = layout;
            ++layout.blockCount;
            ++me.context.blockCount;
        }
    },

    addBoxChild: function (boxChildItem) {
        var me = this,
            children,
            widthModel = boxChildItem.widthModel;

        boxChildItem.boxParent = this;

        
        
        
        
        
        
        boxChildItem.measuresBox = widthModel.shrinkWrap ? boxChildItem.hasRawContent : widthModel.natural;

        if (boxChildItem.measuresBox) {
            children = me.boxChildren;

            if (children) {
                children.push(boxChildItem);
            } else {
                me.boxChildren = [ boxChildItem ];
            }
        }
    },

    
    addPositionStyles: function(styles, props) {
        var x = props.x,
            y = props.y,
            count = 0;

        if (x !== undefined) {
            styles.left = x + 'px';
            ++count;
        }
        if (y !== undefined) {
            styles.top = y + 'px';
            ++count;
        }
        return count;
    },

    
    addTrigger: function (propName, inDom) {
        var me = this,
            name = inDom ? 'domTriggers' : 'triggers',
            collection = me[name] || (me[name] = {}),
            context = me.context,
            layout = context.currentLayout,
            triggers = collection[propName] || (collection[propName] = {});

        if (!triggers[layout.id]) {
            triggers[layout.id] = layout;
            ++layout.triggerCount;

            triggers = context.triggers[inDom ? 'dom' : 'data'];
            (triggers[layout.id] || (triggers[layout.id] = [])).push({
                item: this,
                prop: propName
            });

            if (me.props[propName] !== undefined) {
                if (!inDom || !(me.dirty && (propName in me.dirty))) {
                    ++layout.firedTriggers;
                }
            }
        }
    },

    boxChildMeasured: function () {
        var me = this,
            state = me.state,
            count = (state.boxesMeasured = (state.boxesMeasured || 0) + 1);

        if (count == me.boxChildren.length) {
            
            
            state.clearBoxWidth = 1;
            ++me.context.progressCount;
            me.markDirty();
        }
    },

    borderNames: [ 'border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width'],
    marginNames: [ 'margin-top', 'margin-right', 'margin-bottom', 'margin-left' ],
    paddingNames: [ 'padding-top', 'padding-right', 'padding-bottom', 'padding-left' ],
    trblNames: [ 'top', 'right', 'bottom', 'left' ],

    cacheMissHandlers: {
        borderInfo: function (me) {
            var info = me.getStyles(me.borderNames, me.trblNames);

            info.width = info.left + info.right;
            info.height = info.top + info.bottom;

            return info;
        },

        marginInfo: function (me) {
            var info = me.getStyles(me.marginNames, me.trblNames);

            info.width = info.left + info.right;
            info.height = info.top + info.bottom;

            return info;
        },

        paddingInfo: function (me) {
            
            var item = me.frameBodyContext || me,
                info = item.getStyles(me.paddingNames, me.trblNames);

            info.width = info.left + info.right;
            info.height = info.top + info.bottom;

            return info;
        }
    },

    checkCache: function (entry) {
        return this.cacheMissHandlers[entry](this);
    },

    clearAllBlocks: function (name) {
        var collection = this[name],
            propName;

        if (collection) {
            for (propName in collection) {
                this.clearBlocks(name, propName);
            }
        }
    },

    
    clearBlocks: function (name, propName) {
        var collection = this[name],
            blockedLayouts = collection && collection[propName],
            context, layout, layoutId;

        if (blockedLayouts) {
            delete collection[propName];

            context = this.context;

            for (layoutId in blockedLayouts) {
                layout = blockedLayouts[layoutId];

                --context.blockCount;
                if (! --layout.blockCount && !layout.pending && !layout.done) {
                    context.queueLayout(layout);
                }
            }
        }
    },

    
    block: function (layout, propName) {
        this.addBlock('blocks', layout, propName);
    },

    
    domBlock: function (layout, propName) {
        this.addBlock('domBlocks', layout, propName);
    },

    
    fireTriggers: function (name, propName) {
        var collection = this[name],
            triggers = collection && collection[propName],
            context = this.context,
            layout, layoutId;

        if (triggers) {
            for (layoutId in triggers) {
                layout = triggers[layoutId];
                ++layout.firedTriggers;
                if (!layout.done && !layout.blockCount && !layout.pending) {
                    context.queueLayout(layout);
                }
            }
        }
    },

    
    flush: function () {
        var me = this,
            dirty = me.dirty,
            state = me.state,
            targetEl = me.el;

        me.dirtyCount = 0;

        
        if (me.classList && me.classList.dirty) {
            me.classList.flush();
        }

        
        if ('attributes' in me) {
            targetEl.set(me.attributes);
            delete me.attributes;
        }

        
        if ('innerHTML' in me) {
            targetEl.innerHTML = me.innerHTML;
            delete me.innerHTML;
        }

        if (state && state.clearBoxWidth) {
            state.clearBoxWidth = 0;
            me.el.setStyle('width', null);

            if (! --state.blocks) {
                me.context.queueItemLayouts(me);
            }
        }

        if (dirty) {
            delete me.dirty;
            me.writeProps(dirty, true);
        }
    },

    
    flushAnimations: function() {
        var me = this,
            animateFrom = me.previousSize,
            target, targetAnim, duration, animateProps, anim,
            changeCount, j, propsLen, propName, oldValue, newValue;

        
        if (animateFrom) {
            target = me.target;
            targetAnim = target.layout && target.layout.animate;
            if (targetAnim) {
                duration = Ext.isNumber(targetAnim) ? targetAnim : targetAnim.duration;
            }
            animateProps = Ext.Object.getKeys(me.animatePolicy);

            
            
            anim = Ext.apply({}, {
                from: {},
                to: {},
                duration: duration || Ext.fx.Anim.prototype.duration
            }, targetAnim);

            for (changeCount = 0, j = 0, propsLen = animateProps.length; j < propsLen; j++) {
                propName = animateProps[j];
                oldValue = animateFrom[propName];
                newValue = me.peek(propName);
                if (oldValue != newValue) {
                    propName = me.translateProps[propName]||propName;
                    anim.from[propName] = oldValue;
                    anim.to[propName] = newValue;
                    ++changeCount;
                }
            }

            
            if (changeCount) {
                
                if (me.isCollapsingOrExpanding === 1) {
                    target.componentLayout.undoLayout(me);
                }

                
                else {
                    me.writeProps(anim.from);
                }
                me.el.animate(anim);

                Ext.fx.Manager.getFxQueue(me.el.id)[0].on({
                    afteranimate: function() {
                        if (me.isCollapsingOrExpanding === 1) {
                            target.componentLayout.redoLayout(me);
                            target.afterCollapse(true);
                        } else if (me.isCollapsingOrExpanding === 2) {
                            target.afterExpand(true);
                        }
                    }
                });
            }
        }
    },

    
    getBorderInfo: function () {
        var me = this,
            info = me.borderInfo;

        if (!info) {
            me.borderInfo = info = me.checkCache('borderInfo');
        }

        return info;
    },

    
    getClassList: function () {
        return this.classList || (this.classList = new Ext.layout.ClassList(this));
    },

    
    getEl: function (nameOrEl, owner) {
        var me = this,
            src, el, elContext;

        if (nameOrEl) {
            if (nameOrEl.dom) {
                el = nameOrEl;
            } else {
                src = me.target;
                if (owner) {
                    src = owner;
                }

                el = src[nameOrEl];
                if (typeof el == 'function') { 
                    el = el.call(src);
                    if (el === me.el) {
                        return this; 
                    }
                }
            }

            if (el) {
                elContext = me.context.getEl(me, el);
            }
        }

        return elContext || null;
    },

    
    getFrameInfo: function () {
        var me = this,
            info = me.frameInfo,
            framing, border;

        if (!info) {
            framing = me.framing;
            border = me.getBorderInfo();

            me.frameInfo = info = 
                framing ? {
                    top   : framing.top    + border.top,
                    right : framing.right  + border.right,
                    bottom: framing.bottom + border.bottom,
                    left  : framing.left   + border.left,
                    width : framing.width  + border.width,
                    height: framing.height + border.height
                } : border;
        }

        return info;
    },

    
    getMarginInfo: function () {
        var me = this,
            info = me.marginInfo,
            comp, manageMargins, margins, ownerLayout, ownerLayoutId;

        if (!info) {
            if (!me.wrapsComponent) {
                info = me.checkCache('marginInfo');
            } else {
                comp = me.target;
                ownerLayout = comp.ownerLayout;
                ownerLayoutId = ownerLayout ? ownerLayout.id : null;
                manageMargins = ownerLayout && ownerLayout.manageMargins;

                
                
                
                
                
                
                
                
                

                
                
                
                

                info = comp.margin$;
                if (info && info.ownerId !== ownerLayoutId) {
                    
                    info = null;

                    
                    
                    
                }

                if (!info) { 
                    
                    info = me.parseMargins(comp, comp.margin) || me.checkCache('marginInfo');

                    
                    if (manageMargins) {
                        margins = me.parseMargins(comp, comp.margins, ownerLayout.defaultMargins);

                        if (margins) { 
                            
                            info = {
                                top:    info.top    + margins.top,
                                right:  info.right  + margins.right,
                                bottom: info.bottom + margins.bottom,
                                left:   info.left   + margins.left
                            };
                        }

                        me.setProp('margin-top', 0);
                        me.setProp('margin-right', 0);
                        me.setProp('margin-bottom', 0);
                        me.setProp('margin-left', 0);
                    }

                    
                    info.ownerId = ownerLayoutId;
                    comp.margin$ = info;
                }

                info.width  = info.left + info.right;
                info.height = info.top  + info.bottom;
            }

            me.marginInfo = info;
        }

        return info;
    },

    
    clearMarginCache: function() {
        delete this.marginInfo;
        delete this.target.margin$;
    },

    
    getPaddingInfo: function () {
        var me = this,
            info = me.paddingInfo;

        if (!info) {
            me.paddingInfo = info = me.checkCache('paddingInfo');
        }

        return info;
    },

    
    getProp: function (propName) {
        var me = this,
            result = me.props[propName];

        me.addTrigger(propName);
        return result;
    },

    
    getDomProp: function (propName) {
        var me = this,
            result = (me.dirty && (propName in me.dirty)) ? undefined : me.props[propName];

        me.addTrigger(propName, true);
        return result;
    },

    
    getStyle: function (styleName) {
        var me = this,
            styles = me.styles,
            info, value;

        if (styleName in styles) {
            value = styles[styleName];
        } else {
            info = me.styleInfo[styleName];
            value = me.el.getStyle(styleName);

            if (info && info.parseInt) {
                value = parseInt(value, 10) || 0;
            }

            styles[styleName] = value;
        }

        return value;
    },

    
    getStyles: function (styleNames, altNames) {
        var me = this,
            styleCache = me.styles,
            values = {},
            hits = 0,
            n = styleNames.length,
            i, missing, missingAltNames, name, info, styleInfo, styles, value;

        altNames = altNames || styleNames;

        
        
        for (i = 0; i < n; ++i) {
            name = styleNames[i];

            if (name in styleCache) {
                values[altNames[i]] = styleCache[name];
                ++hits;

                if (i && hits==1) { 
                    missing = styleNames.slice(0, i);
                    missingAltNames = altNames.slice(0, i);
                }
            } else if (hits) {
                (missing || (missing = [])).push(name);
                (missingAltNames || (missingAltNames = [])).push(altNames[i]);
            }
        }

        if (hits < n) {
            missing = missing || styleNames;
            missingAltNames = missingAltNames || altNames;
            styleInfo = me.styleInfo;

            styles = me.el.getStyle(missing);

            for (i = missing.length; i--; ) {
                name = missing[i];
                info = styleInfo[name];
                value = styles[name];

                if (info && info.parseInt) {
                    value = parseInt(value, 10) || 0;
                }

                values[missingAltNames[i]] = value;
                styleCache[name] = value;
            }
        }

        return values;
    },

    
    hasProp: function (propName) {
        return this.getProp(propName) != null;
    },

    
    hasDomProp: function (propName) {
        return this.getDomProp(propName) != null;
    },

    
    invalidate: function (options) {
        this.context.queueInvalidate(this, options);
    },

    markDirty: function () {
        if (++this.dirtyCount == 1) {
            
            this.context.queueFlush(this);
        }
    },

    onBoxMeasured: function () {
        var boxParent = this.boxParent,
            state = this.state;

        if (boxParent && boxParent.widthModel.shrinkWrap && !state.boxMeasured && this.measuresBox) {
            
            
            state.boxMeasured = 1; 
            boxParent.boxChildMeasured();
        }
    },

    parseMargins: function (comp, margins, defaultMargins) {
        if (margins === true) {
            margins = 5;
        }

        var type = typeof margins,
            ret;

        if (type == 'string' || type == 'number') {
            ret = comp.parseBox(margins);
        } else if (margins || defaultMargins) {
            ret = { top: 0, right: 0, bottom: 0, left: 0 }; 

            if (defaultMargins) {
                Ext.apply(ret, this.parseMargins(comp, defaultMargins)); 
            }

            if (margins) {
                margins = Ext.apply(ret, comp.parseBox(margins)); 
            }
        }

        return ret;
    },

    peek: function (propName) {
        return this.props[propName];
    },

    
    recoverProp: function (propName, oldProps, oldDirty) {
        var me = this,
            props = me.props,
            dirty;

        if (propName in oldProps) {
            props[propName] = oldProps[propName];

            if (oldDirty && propName in oldDirty) {
                dirty = me.dirty || (me.dirty = {});
                dirty[propName] = oldDirty[propName];
            }
        }
    },

    redo: function(deep) {
        var me = this,
            items, len, i;

        me.revertProps(me.props);

        if (deep && me.wrapsComponent) {
            
            if (me.childItems) {
                for (i = 0, items = me.childItems, len = items.length; i < len; i++) {
                    items[i].redo(deep);
                }
            }

            
            for (i = 0, items = me.children, len = items.length; i < len; i++) {
                items[i].redo();
            }
        }
    },

    
    removeEl: function(nameOrEl, owner) {
        var me = this,
            src, el;

        if (nameOrEl) {
            if (nameOrEl.dom) {
                el = nameOrEl;
            } else {
                src = me.target;
                if (owner) {
                    src = owner;
                }

                el = src[nameOrEl];
                if (typeof el == 'function') { 
                    el = el.call(src);
                    if (el === me.el) {
                        return this; 
                    }
                }
            }

            if (el) {
                me.context.removeEl(me, el);
            }
        }
    },

    revertProps: function (props) {
        var name,
            flushed = this.flushedProps,
            reverted = {};

        for (name in props) {
            if (flushed.hasOwnProperty(name)) {
                reverted[name] = props[name];
            }
        }

        this.writeProps(reverted);
    },

    
    setAttribute: function(name, value) {
        var me = this;
        if (!me.attributes) {
            me.attributes = {};
        }
        me.attributes[name] = value;
        me.markDirty();
    },

    setBox: function (box) {
        var me = this;

        if ('left' in box) {
            me.setProp('x', box.left);
        }
        if ('top' in box) {
            me.setProp('y', box.top);
        }

        
        
        
        me.setSize(box.width, box.height);
    },

    
    setContentHeight: function (height, measured) {
        if (!measured && this.hasRawContent) {
            return 1;
        }

        return this.setProp('contentHeight', height);
    },

    
    setContentWidth: function (width, measured) {
        if (!measured && this.hasRawContent) {
            return 1;
        }

        return this.setProp('contentWidth', width);
    },

    
    setContentSize: function (width, height, measured) {
        return this.setContentWidth(width, measured) +
               this.setContentHeight(height, measured) == 2;
    },

    
    setProp: function (propName, value, dirty) {
        var me = this,
            valueType = typeof value,
            borderBox, info;

        if (valueType == 'undefined' || (valueType === 'number' && isNaN(value))) {
            return 0;
        }
        if (me.props[propName] === value) {
            return 1;
        }

        me.props[propName] = value;
        ++me.context.progressCount;

        if (dirty === false) {
            
            
            me.fireTriggers('domTriggers', propName);
            me.clearBlocks('domBlocks', propName);
        } else {
            info = me.styleInfo[propName];
            if (info) {
                if (!me.dirty) {
                    me.dirty = {};
                }

                if (propName == 'width' || propName == 'height') {
                    borderBox = me.isBorderBoxValue;
                    if (borderBox === null) {
                        me.isBorderBoxValue = borderBox = !!me.el.isBorderBox();
                    }

                    if (!borderBox) {
                        me.borderInfo || me.getBorderInfo();
                        me.paddingInfo || me.getPaddingInfo();
                    }
                }
                me.dirty[propName] = value;
                me.markDirty();
            }
        }

        
        me.fireTriggers('triggers', propName);
        me.clearBlocks('blocks', propName);
        return 1;
    },

    
    setHeight: function (height, dirty ) {
        var me = this,
            comp = me.target,
            ownerCtContext = me.ownerCtContext,
            frameBody, frameInfo, min, oldHeight, rem;

        if (height < 0) {
            height = 0;
        }
        if (!me.wrapsComponent) {
            if (!me.setProp('height', height, dirty)) {
                return NaN;
            }
        } else {
            min = me.collapsedVert ? 0 : (comp.minHeight || 0);
            height = Ext.Number.constrain(height, min, comp.maxHeight);
            oldHeight = me.props.height;
            if (!me.setProp('height', height, dirty)) {
                return NaN;
            }

            
            
            if (ownerCtContext && !me.isComponentChild && isNaN(oldHeight)) {
                rem = --ownerCtContext.remainingChildDimensions;
                if (!rem) {
                    
                    
                    
                    ownerCtContext.setProp('containerChildrenSizeDone', true);
                }
            }

            frameBody = me.frameBodyContext;
            if (frameBody){
                frameInfo = me.getFrameInfo();
                frameBody.setHeight(height - frameInfo.height, dirty);
            }
        }

        return height;
    },

    
    setWidth: function (width, dirty ) {
        var me = this,
            comp = me.target,
            ownerCtContext = me.ownerCtContext,
            frameBody, frameInfo, min, oldWidth, rem;

        if (width < 0) {
            width = 0;
        }
        if (!me.wrapsComponent) {
            if (!me.setProp('width', width, dirty)) {
                return NaN;
            }
        } else {
            min = me.collapsedHorz ? 0 : (comp.minWidth || 0);
            width = Ext.Number.constrain(width, min, comp.maxWidth);
            oldWidth = me.props.width
            if (!me.setProp('width', width, dirty)) {
                return NaN;
            }

            
            
            if (ownerCtContext && !me.isComponentChild && isNaN(oldWidth)) {
                rem = --ownerCtContext.remainingChildDimensions;
                if (!rem) {
                    
                    
                    
                    ownerCtContext.setProp('containerChildrenSizeDone', true);
                }
            }

            
            frameBody = me.frameBodyContext;
            if (frameBody) {
                frameInfo = me.getFrameInfo();
                frameBody.setWidth(width - frameInfo.width, dirty);
            }

            
        }

        return width;
    },

    setSize: function (width, height, dirty) {
        this.setWidth(width, dirty);
        this.setHeight(height, dirty);
    },

    translateProps: {
        x: 'left',
        y: 'top'
    },

    undo: function(deep) {
        var me = this,
            items, len, i;

        me.revertProps(me.lastBox);

        if (deep && me.wrapsComponent) {
            
            if (me.childItems) {
                for (i = 0, items = me.childItems, len = items.length; i < len; i++) {
                    items[i].undo(deep);
                }
            }

            
            for (i = 0, items = me.children, len = items.length; i < len; i++) {
                items[i].undo();
            }
        }
    },

    unsetProp: function (propName) {
        var dirty = this.dirty;

        delete this.props[propName];
        if (dirty) {
            delete dirty[propName];
        }
    },

    writeProps: function(dirtyProps, flushing) {
        if (!(dirtyProps && typeof dirtyProps == 'object')) {
            return;
        }

        var me = this,
            el = me.el,
            styles = {},
            styleCount = 0, 
            styleInfo = me.styleInfo,

            info,
            propName,
            numericValue,
            width = dirtyProps.width,
            height = dirtyProps.height,
            isBorderBox = me.isBorderBoxValue,
            target = me.target,
            max = Math.max,
            paddingWidth = 0,
            paddingHeight = 0,
            hasWidth, hasHeight, isAbsolute, scrollbarSize, style, targetEl;

        
        if ('displayed' in dirtyProps) {
            el.setDisplayed(dirtyProps.displayed);
        }

        
        for (propName in dirtyProps) {
            if (flushing) {
                me.fireTriggers('domTriggers', propName);
                me.clearBlocks('domBlocks', propName);
                me.flushedProps[propName] = 1;
            }

            info = styleInfo[propName];
            if (info && info.dom) {
                
                if (info.suffix && (numericValue = parseInt(dirtyProps[propName], 10))) {
                    styles[propName] = numericValue + info.suffix;
                }
                
                else {
                    styles[propName] = dirtyProps[propName];
                }
                ++styleCount;
            }
        }

        
        if ('x' in dirtyProps || 'y' in dirtyProps) {
            if (target.isComponent) {
                target.setPosition(dirtyProps.x, dirtyProps.y);
            } else {
                
                styleCount += me.addPositionStyles(styles, dirtyProps);
            }
        }

        
        if (!isBorderBox && (width > 0 || height > 0)) { 
            
            
            if(!me.frameBodyContext) {
                
                paddingWidth = me.paddingInfo.width;
                paddingHeight = me.paddingInfo.height;
            }
            if (width) {
                width = max(parseInt(width, 10) - (me.borderInfo.width + paddingWidth), 0);
                styles.width = width + 'px';
                ++styleCount;
            }
            if (height) {
                height = max(parseInt(height, 10) - (me.borderInfo.height + paddingHeight), 0);
                styles.height = height + 'px';
                ++styleCount;
            }
        }

        
        
        
        
        
        
        if (me.wrapsComponent && Ext.isIE9 && Ext.isStrict) {
            
            
            if ((hasWidth = width !== undefined && me.hasOverflowY) ||
                (hasHeight = height !== undefined && me.hasOverflowX)) {
                
                isAbsolute = me.isAbsolute;
                if (isAbsolute === undefined) {
                    isAbsolute = false;
                    targetEl = me.target.getTargetEl();
                    style = targetEl.getStyle('position');

                    if (style == 'absolute') {
                        style = targetEl.getStyle('box-sizing');
                        isAbsolute = (style == 'border-box');
                    }

                    me.isAbsolute = isAbsolute; 
                }

                if (isAbsolute) {
                    scrollbarSize = Ext.getScrollbarSize();

                    if (hasWidth) {
                        width = parseInt(width, 10) + scrollbarSize.width;
                        styles.width = width + 'px';
                        ++styleCount;
                    }
                    if (hasHeight) {
                        height = parseInt(height, 10) + scrollbarSize.height;
                        styles.height = height + 'px';
                        ++styleCount;
                    }
                }
            }
        }

        
        if (styleCount) {
            el.setStyle(styles);
        }
    }
}, function () {

    var px =    { dom: true, parseInt: true, suffix: 'px' },
        isDom = { dom: true },
        faux =  { dom: false };

    
    
    
    
    
    
    
    
    this.prototype.styleInfo = {
        containerChildrenSizeDone:  faux,
        containerLayoutDone:    faux,
        displayed:              faux,
        done:                   faux,
        x:                      faux,
        y:                      faux,

        
        columnWidthsDone:       faux,

        left:                   px,
        top:                    px,
        right:                  px,
        bottom:                 px,
        width:                  px,
        height:                 px,

        'border-top-width':     px,
        'border-right-width':   px,
        'border-bottom-width':  px,
        'border-left-width':    px,

        'margin-top':           px,
        'margin-right':         px,
        'margin-bottom':        px,
        'margin-left':          px,

        'padding-top':          px,
        'padding-right':        px,
        'padding-bottom':       px,
        'padding-left':         px,

        'line-height':          isDom,
        display:                isDom
    };
});


Ext.define('Ext.layout.Context', {
               
                         
                                 
                            
                      
                        
      

    remainingLayouts: 0,

    
    state: 0,

    constructor: function (config) {
        var me = this;

        Ext.apply(me, config);

        
        me.items = {};

        
        me.layouts = {};

        
        me.blockCount = 0;
        
        me.cycleCount = 0;
        
        me.flushCount = 0;
        
        me.calcCount = 0;

        me.animateQueue = me.newQueue();
        me.completionQueue = me.newQueue();
        me.finalizeQueue = me.newQueue();
        me.finishQueue = me.newQueue();
        me.flushQueue = me.newQueue();

        me.invalidateData = {};

        
        me.layoutQueue = me.newQueue();

        
        
        me.invalidQueue = [];

        me.triggers = {
            data: {
                
            },
            dom: {}
        };
    },

    callLayout: function (layout, methodName) {
        this.currentLayout = layout;
        layout[methodName](this.getCmp(layout.owner));
    },

    cancelComponent: function (comp, isChild, isDestroying) {
        var me = this,
            components = comp,
            isArray = !comp.isComponent,
            length = isArray ? components.length : 1,
            i, k, klen, items, layout, newQueue, oldQueue, entry, temp,
            ownerCtContext;

        for (i = 0; i < length; ++i) {
            if (isArray) {
                comp = components[i];
            }

            
            if (isDestroying && comp.ownerCt) {
                ownerCtContext = this.items[comp.ownerCt.el.id];
                if (ownerCtContext) {
                    Ext.Array.remove(ownerCtContext.childItems, me.getCmp(comp));
                }
            }

            if (!isChild) {
                oldQueue = me.invalidQueue;
                klen = oldQueue.length;

                if (klen) {
                    me.invalidQueue = newQueue = [];
                    for (k = 0; k < klen; ++k) {
                        entry = oldQueue[k];
                        temp = entry.item.target;
                        if (temp != comp && !temp.isDescendant(comp)) {
                            newQueue.push(entry);
                        }
                    }
                }
            }

            layout = comp.componentLayout;
            me.cancelLayout(layout);

            if (layout.getLayoutItems) {
                items = layout.getLayoutItems();
                if (items.length) {
                    me.cancelComponent(items, true);
                }
            }

            if (comp.isContainer && !comp.collapsed) {
                layout = comp.layout;
                me.cancelLayout(layout);

                items = layout.getVisibleItems();
                if (items.length) {
                    me.cancelComponent(items, true);
                }
            }
        }
    },

    cancelLayout: function (layout) {
        var me = this;

        me.completionQueue.remove(layout);
        me.finalizeQueue.remove(layout);
        me.finishQueue.remove(layout);
        me.layoutQueue.remove(layout);

        if (layout.running) {
            me.layoutDone(layout);
        }

        layout.ownerContext = null;
    },

    clearTriggers: function (layout, inDom) {
        var id = layout.id,
            collection = this.triggers[inDom ? 'dom' : 'data'],
            triggers = collection && collection[id],
            length = (triggers && triggers.length) || 0,
            i, item, trigger;

        for (i = 0; i < length; ++i) {
            trigger = triggers[i];
            item = trigger.item;

            collection = inDom ? item.domTriggers : item.triggers;
            delete collection[trigger.prop][id];
        }
    },

    
    flush: function () {
        var me = this,
            items = me.flushQueue.clear(),
            length = items.length, i;

        if (length) {
            ++me.flushCount;

            for (i = 0; i < length; ++i) {
                items[i].flush();
            }
        }
    },

    flushAnimations: function() {
        var me = this,
            items = me.animateQueue.clear(),
            len = items.length,
            i;

        if (len) {
            for (i = 0; i < len; i++) {
                
                
                
                if (items[i].target.animate !== false) {
                    items[i].flushAnimations();
                }
            }

            
            
            Ext.fx.Manager.runner();
        }
    },

    flushInvalidates: function () {
        var me = this,
            queue = me.invalidQueue,
            length = queue && queue.length,
            comp, components, entry, i;

        me.invalidQueue = [];

        if (length) {
            components = [];
            for (i = 0; i < length; ++i) {
                comp = (entry = queue[i]).item.target;
                
                
                
                
                if (!comp.container.isDetachedBody) {
                    components.push(comp);

                    if (entry.options) {
                        me.invalidateData[comp.id] = entry.options;
                    }
                }
            }

            me.invalidate(components, null);
        }
    },

    flushLayouts: function (queueName, methodName, dontClear) {
        var me = this,
            layouts = dontClear ? me[queueName].items : me[queueName].clear(),
            length = layouts.length,
            i, layout;

        if (length) {
            for (i = 0; i < length; ++i) {
                layout = layouts[i];
                if (!layout.running) {
                    me.callLayout(layout, methodName);
                }
            }
            me.currentLayout = null;
        }
    },

    
    getCmp: function (cmp) {
        return this.getItem(cmp, cmp.el);
    },

    
    getEl: function (parent, el) {
        var item = this.getItem(el, el);

        if (!item.parent) {
            item.parent = parent;

            
            
            
            if (parent.children.length) {
                parent.children.push(item);
            } else {
                parent.children = [ item ]; 
            }
        }

        return item;
    },

    getItem: function (target, el) {
        var id = el.id,
            items = this.items,
            item = items[id] ||
                  (items[id] = new Ext.layout.ContextItem({
                                    context: this,
                                    target: target,
                                    el: el
                                }));

        return item;
    },

    handleFailure: function () {
        
        
        
        var layouts = this.layouts,
            layout, key;

        Ext.failedLayouts = (Ext.failedLayouts || 0) + 1;

        for (key in layouts) {
            layout = layouts[key];

            if (layouts.hasOwnProperty(key)) {
                layout.running      = false;
                layout.ownerContext = null;
            }
        }

    },

    
    invalidate: function (components, full) {
        var me = this,
            isArray = !components.isComponent,
            containerLayoutDone,
            firstTime, i, comp, item, items, length, componentLayout, layout,
            invalidateOptions, token;

        for (i = 0, length = isArray ? components.length : 1; i < length; ++i) {
            comp = isArray ? components[i] : components;

            if (comp.rendered && !comp.hidden) {
                item = me.getCmp(comp);
                
                
                




                componentLayout = comp.componentLayout;
                firstTime = !componentLayout.ownerContext;
                layout = (comp.isContainer && !comp.collapsed) ? comp.layout : null;

                
                invalidateOptions = me.invalidateData[item.id];
                delete me.invalidateData[item.id];

                
                
                
                
                
                
                token = item.init(full, invalidateOptions);

                if (invalidateOptions) {
                    me.processInvalidate(invalidateOptions, item, 'before');
                }

                
                
                
                if (componentLayout.beforeLayoutCycle) {
                    componentLayout.beforeLayoutCycle(item);
                }
                
                if (layout && layout.beforeLayoutCycle) {
                    
                    
                    
                    
                    layout.beforeLayoutCycle(item);
                }

                
                
                token = item.initContinue(token);

                
                
                containerLayoutDone = true;

                
                
                
                
                if (componentLayout.getLayoutItems) {
                    componentLayout.renderChildren();

                    items = componentLayout.getLayoutItems();
                    if (items.length) {
                        me.invalidate(items, true);
                    }
                }

                if (layout) {
                    containerLayoutDone = false;
                    layout.renderChildren();

                    items = layout.getVisibleItems();
                    if (items.length) {
                        me.invalidate(items, true);
                    }
                }

                
                
                item.initDone(containerLayoutDone);

                
                
                me.resetLayout(componentLayout, item, firstTime);
                if (layout) {
                    me.resetLayout(layout, item, firstTime);
                }

                
                
                
                item.initAnimation();

                if (invalidateOptions) {
                    me.processInvalidate(invalidateOptions, item, 'after');
                }
            }
        }

        me.currentLayout = null;
    },

    layoutDone: function (layout) {
        var ownerContext = layout.ownerContext;

        layout.running = false;

        
        if (layout.isComponentLayout) {
            if (ownerContext.measuresBox) {
                ownerContext.onBoxMeasured(); 
            }

            ownerContext.setProp('done', true);
        } else {
            ownerContext.setProp('containerLayoutDone', true);
        }

        --this.remainingLayouts;
        ++this.progressCount; 
    },

    newQueue: function () {
        return new Ext.util.Queue();
    },

    processInvalidate: function (options, item, name) {
        
        
        if (options[name]) {
            var me = this,
                currentLayout = me.currentLayout;

            me.currentLayout = options.layout || null;

            options[name](item, options);

            me.currentLayout = currentLayout;
        }
    },

    
    queueAnimation: function (item) {
        this.animateQueue.add(item);
    },

    
    queueCompletion: function (layout) {
        this.completionQueue.add(layout);
    },

    
    queueFinalize: function (layout) {
        this.finalizeQueue.add(layout);
    },

    
    queueFlush: function (item) {
        this.flushQueue.add(item);
    },

    chainFns: function (oldOptions, newOptions, funcName) {
        var me = this,
            oldLayout = oldOptions.layout,
            newLayout = newOptions.layout,
            oldFn = oldOptions[funcName],
            newFn = newOptions[funcName];

        
        
        return function (contextItem) {
            var prev = me.currentLayout;
            if (oldFn) {
                me.currentLayout = oldLayout;
                oldFn.call(oldOptions.scope || oldOptions, contextItem, oldOptions);
            }
            me.currentLayout = newLayout;
            newFn.call(newOptions.scope || newOptions, contextItem, newOptions);
            me.currentLayout = prev;
        };
    },

    
    queueInvalidate: function (item, options) {
        var me = this,
            newQueue = [],
            oldQueue = me.invalidQueue,
            index = oldQueue.length,
            comp, old, oldComp, oldOptions, oldState;

        if (item.isComponent) {
            item = me.getCmp(comp = item);
        } else {
            comp = item.target;
        }

        item.invalid = true;

        
        
        
        while (index--) {
            old = oldQueue[index];
            oldComp = old.item.target;

            if (comp.isDescendant(oldComp)) {
                return; 
            }

            if (oldComp == comp) {
                
                if (!(oldOptions = old.options)) {
                    old.options = options;
                } else if (options) {
                    if (options.widthModel) {
                        oldOptions.widthModel = options.widthModel;
                    }
                    if (options.heightModel) {
                        oldOptions.heightModel = options.heightModel;
                    }
                    if (!(oldState = oldOptions.state)) {
                        oldOptions.state = options.state;
                    } else if (options.state) {
                        Ext.apply(oldState, options.state);
                    }

                    if (options.before) {
                        oldOptions.before = me.chainFns(oldOptions, options, 'before');
                    }
                    if (options.after) {
                        oldOptions.after = me.chainFns(oldOptions, options, 'after');
                    }
                }

                
                return;
            }

            if (!oldComp.isDescendant(comp)) {
                newQueue.push(old); 
            }
            
        }
        

        
        
        newQueue.push({ item: item, options: options });

        me.invalidQueue = newQueue;
    },

    queueItemLayouts: function (item) {
        var comp = item.isComponent ? item : item.target,
            layout = comp.componentLayout;

        if (!layout.pending && !layout.invalid && !layout.done) {
            this.queueLayout(layout);
        }

        layout = comp.layout;
        if (layout && !layout.pending && !layout.invalid && !layout.done) {
            this.queueLayout(layout);
        }
    },

    
    queueLayout: function (layout) {
        this.layoutQueue.add(layout);
        layout.pending = true;
    },

    
    removeEl: function (parent, el) {
        var id = el.id,
            children = parent.children,
            items = this.items;

        if(children) {
            Ext.Array.remove(children, items[id]);
        }
        delete items[id];
    },

    
    resetLayout: function (layout, ownerContext, firstTime) {
        var me = this;

        me.currentLayout = layout;

        layout.done = false;
        layout.pending = true;
        layout.firedTriggers = 0;

        me.layoutQueue.add(layout);

        if (firstTime) {
            me.layouts[layout.id] = layout; 
            layout.running = true;

            if (layout.finishedLayout) {
                me.finishQueue.add(layout);
            }

            

            ++me.remainingLayouts;
            ++layout.layoutCount; 

            layout.ownerContext = ownerContext;
            layout.beginCount = 0; 
            layout.blockCount = 0; 
            layout.calcCount = 0; 
            layout.triggerCount = 0; 

            if (!layout.initialized) {
                layout.initLayout();
            }

            layout.beginLayout(ownerContext);
        } else {
            ++layout.beginCount;

            if (!layout.running) {
                
                ++me.remainingLayouts;
                layout.running = true;

                if (layout.isComponentLayout) {
                    
                    
                    
                    ownerContext.unsetProp('done');
                }

                
                me.completionQueue.remove(layout);
                me.finalizeQueue.remove(layout);
            }
        }

        layout.beginLayoutCycle(ownerContext, firstTime);
    },

    
    run: function () {
        var me = this,
            flushed = false,
            watchDog = 100;

        me.flushInvalidates();

        me.state = 1;
        me.totalCount = me.layoutQueue.getCount();

        
        
        
        
        
        
        me.flush();

        
        while ((me.remainingLayouts || me.invalidQueue.length) && watchDog--) {
            if (me.invalidQueue.length) {
                me.flushInvalidates();
            }

            
            if (me.runCycle()) {
                flushed = false; 
                
            } else if (!flushed) {
                
                
                me.flush();
                flushed = true; 

                me.flushLayouts('completionQueue', 'completeLayout');
            } else if (!me.invalidQueue.length) {
                
                me.state = 2;
                break;
            }

            if (!(me.remainingLayouts || me.invalidQueue.length)) {
                me.flush();
                me.flushLayouts('completionQueue', 'completeLayout');
                me.flushLayouts('finalizeQueue', 'finalizeLayout');
            }
        }

        return me.runComplete();
    },

    runComplete: function () {
        var me = this;

        me.state = 2;

        if (me.remainingLayouts) {
            me.handleFailure();
            return false;
        }

        me.flush();

        
        me.flushLayouts('finishQueue', 'finishedLayout', true);

        
        me.flushLayouts('finishQueue', 'notifyOwner');

        me.flush(); 

        me.flushAnimations();

        return true;
    },

    
    runCycle: function () {
        var me = this,
            layouts = me.layoutQueue.clear(),
            length = layouts.length,
            i;

        ++me.cycleCount;

        
        
        me.progressCount = 0;

        for (i = 0; i < length; ++i) {
            me.runLayout(me.currentLayout = layouts[i]);
        }

        me.currentLayout = null;

        return me.progressCount > 0;
    },

    
    runLayout: function (layout) {
        var me = this,
            ownerContext = me.getCmp(layout.owner);

        layout.pending = false;

        if (ownerContext.state.blocks) {
            return;
        }

        
        
        
        
        layout.done = true;

        ++layout.calcCount;
        ++me.calcCount;

        layout.calculate(ownerContext);

        if (layout.done) {
            me.layoutDone(layout);

            if (layout.completeLayout) {
                me.queueCompletion(layout);
            }
            if (layout.finalizeLayout) {
                me.queueFinalize(layout);
            }
        } else if (!layout.pending && !layout.invalid && !(layout.blockCount + layout.triggerCount - layout.firedTriggers)) {
            
            
            me.queueLayout(layout);
        }
    },

    
    setItemSize: function(item, width, height) {
        var items = item,
            len = 1,
            contextItem, i;

        
        
        
        
        

        if (item.isComposite) {
            items = item.elements;
            len = items.length;
            item = items[0];
        } else if (!item.dom && !item.el) { 
            len = items.length;
            item = items[0];
        }
        

        for (i = 0; i < len; ) {
            contextItem = this.get(item);
            contextItem.setSize(width, height);

            item = items[++i]; 
        }
    }
});














Ext.define('Ext.layout.component.Body', {

    

    alias: ['layout.body'],

    extend:  Ext.layout.component.Auto ,

    

    type: 'body',

    beginLayout: function (ownerContext) {
        this.callParent(arguments);

        ownerContext.bodyContext = ownerContext.getEl('body');
    },

    beginLayoutCycle: function(ownerContext, firstCycle){
        var me = this,
            lastWidthModel = me.lastWidthModel,
            lastHeightModel = me.lastHeightModel,
            body = me.owner.body;

        me.callParent(arguments);

        if (lastWidthModel && lastWidthModel.fixed && ownerContext.widthModel.shrinkWrap) {
            body.setWidth(null);
        }

        if (lastHeightModel && lastHeightModel.fixed && ownerContext.heightModel.shrinkWrap) {
            body.setHeight(null);
        }
    },

    
    
    
    
    
    

    calculateOwnerHeightFromContentHeight: function (ownerContext, contentHeight) {
        var height = this.callParent(arguments);

        if (ownerContext.targetContext != ownerContext) {
            height += ownerContext.getPaddingInfo().height;
        }

        return height;
    },

    calculateOwnerWidthFromContentWidth: function (ownerContext, contentWidth) {
        var width = this.callParent(arguments);

        if (ownerContext.targetContext != ownerContext) {
            width += ownerContext.getPaddingInfo().width;
        }

        return width;
    },

    measureContentWidth: function (ownerContext) {
        return ownerContext.bodyContext.setWidth(ownerContext.bodyContext.el.dom.offsetWidth, false);
    },

    measureContentHeight: function (ownerContext) {
        return ownerContext.bodyContext.setHeight(ownerContext.bodyContext.el.dom.offsetHeight, false);
    },

    publishInnerHeight: function (ownerContext, height) {
        var innerHeight = height - ownerContext.getFrameInfo().height,
            targetContext = ownerContext.targetContext;

        if (targetContext != ownerContext) {
            innerHeight -= ownerContext.getPaddingInfo().height;
        }

        
        return ownerContext.bodyContext.setHeight(innerHeight, !ownerContext.heightModel.natural);
    },

    publishInnerWidth: function (ownerContext, width) {
        var innerWidth = width - ownerContext.getFrameInfo().width,
            targetContext = ownerContext.targetContext;

        if (targetContext != ownerContext) {
            innerWidth -= ownerContext.getPaddingInfo().width;
        }

        ownerContext.bodyContext.setWidth(innerWidth, !ownerContext.widthModel.natural);
    }
});


Ext.define('Ext.layout.component.FieldSet', {
    extend:  Ext.layout.component.Body ,
    alias: ['layout.fieldset'],

    type: 'fieldset',
    
    defaultCollapsedWidth: 100,

    beforeLayoutCycle: function (ownerContext) {
        if (ownerContext.target.collapsed) {
            ownerContext.heightModel = this.sizeModels.shrinkWrap;
        }
    },

    beginLayoutCycle: function (ownerContext) {
        var target = ownerContext.target,
            lastSize;

        this.callParent(arguments);

        
        
        
        if (target.collapsed) {
            ownerContext.setContentHeight(0);
            
            
            ownerContext.restoreMinHeight = target.minHeight;
            delete target.minHeight;

            
            
            
            if (ownerContext.widthModel.shrinkWrap) {
                lastSize = target.lastComponentSize;
                ownerContext.setContentWidth((lastSize && lastSize.contentWidth) || this.defaultCollapsedWidth);
            }
        }
    },
    
    finishedLayout: function(ownerContext) {
        var owner = this.owner,
            restore = ownerContext.restoreMinHeight;
             
        this.callParent(arguments);
        if (restore) {
            owner.minHeight = restore;
        }
    },

    calculateOwnerHeightFromContentHeight: function (ownerContext, contentHeight) {
        var border = ownerContext.getBorderInfo(),
            legend = ownerContext.target.legend;
            
        
        
        return ownerContext.getProp('contentHeight') +
               ownerContext.getPaddingInfo().height +
               
               ((Ext.isIEQuirks || Ext.isIE8m) ?
                   ownerContext.bodyContext.getPaddingInfo().top : 0) +
               (legend ? legend.getHeight() : border.top) +
               border.bottom;
    },

    publishInnerHeight: function (ownerContext, height) {
        
        
        
        var legend = ownerContext.target.legend;
        if (legend) {
            height -= legend.getHeight();
        }
        this.callParent([ownerContext, height]);
    },

    getLayoutItems : function() {
        var legend = this.owner.legend;
        return legend ? [legend] : [];
    }
});


Ext.define('Ext.layout.component.field.Slider', {

    

    alias: ['layout.sliderfield'],

    extend:  Ext.layout.component.field.Field ,

    

    type: 'sliderfield',

    beginLayout: function(ownerContext) {
        this.callParent(arguments);

        ownerContext.endElContext   = ownerContext.getEl('endEl');
        ownerContext.innerElContext = ownerContext.getEl('innerEl');
        ownerContext.bodyElContext  = ownerContext.getEl('bodyEl');
    },

    publishInnerHeight: function (ownerContext, height) {
        var innerHeight = height - this.measureLabelErrorHeight(ownerContext),
            endElPad,
            inputPad;
        if (this.owner.vertical) {
            endElPad = ownerContext.endElContext.getPaddingInfo();
            inputPad = ownerContext.inputContext.getPaddingInfo();
            ownerContext.innerElContext.setHeight(innerHeight - inputPad.height - endElPad.height);
        } else {
            ownerContext.bodyElContext.setHeight(innerHeight);
        }
    },

    publishInnerWidth: function (ownerContext, width) {
        if (!this.owner.vertical) {
            var endElPad = ownerContext.endElContext.getPaddingInfo(),
                inputPad = ownerContext.inputContext.getPaddingInfo();

            ownerContext.innerElContext.setWidth(width - inputPad.left - endElPad.right - ownerContext.labelContext.getProp('width'));
        }
    },

    beginLayoutFixed: function(ownerContext, width, suffix) {
        var me = this,
            ieInputWidthAdjustment = me.ieInputWidthAdjustment;

        if (ieInputWidthAdjustment) {
            
            
            me.owner.bodyEl.setStyle('padding-right', ieInputWidthAdjustment + 'px');
        }

        me.callParent(arguments);
    }
});


Ext.define('Ext.layout.container.Absolute', {

    

    alias: 'layout.absolute',
    extend:  Ext.layout.container.Anchor ,
    alternateClassName: 'Ext.layout.AbsoluteLayout',

    

    targetCls: Ext.baseCSSPrefix + 'abs-layout-ct',
    itemCls: Ext.baseCSSPrefix + 'abs-layout-item',

    
    ignoreOnContentChange: true,

    type: 'absolute',

    
    adjustWidthAnchor: function(value, childContext) {
        var padding = this.targetPadding,
            x = childContext.getStyle('left');

        return value - x + padding.left;
    },

    
    adjustHeightAnchor: function(value, childContext) {
        var padding = this.targetPadding,
            y = childContext.getStyle('top');

        return value - y + padding.top;
    },

    isItemLayoutRoot: function (item) {
        return this.ignoreOnContentChange || this.callParent(arguments);
    },

    isItemShrinkWrap: function (item) {
        return true;
    },

    beginLayout: function (ownerContext) {
        var me = this,
            target = me.getTarget();

        me.callParent(arguments);

        
        if (target.dom !== document.body) {
            target.position();
        }

        me.targetPadding = ownerContext.targetContext.getPaddingInfo();
    },

    isItemBoxParent: function (itemContext) {
        return true;
    },

    onContentChange: function () {
        if (this.ignoreOnContentChange) {
            return false;
        }
        return this.callParent(arguments);
    },

    calculateContentSize: function (ownerContext, dimensions) {
        var me = this,
            containerDimensions = (dimensions || 0) |
                   ((ownerContext.widthModel.shrinkWrap ? 1 : 0) |
                    (ownerContext.heightModel.shrinkWrap ? 2 : 0)),
            calcWidth = (containerDimensions & 1) || undefined,
            calcHeight = (containerDimensions & 2) || undefined,
            childItems = ownerContext.childItems,
            length = childItems.length,
            contentHeight = 0,
            contentWidth = 0,
            needed = 0,
            props = ownerContext.props,
            targetPadding, child, childContext, height, i, margins, width;

        if (calcWidth) {
            if (isNaN(props.contentWidth)) {
                ++needed;
            } else {
                calcWidth = undefined;
            }
        }
        if (calcHeight) {
            if (isNaN(props.contentHeight)) {
                ++needed;
            } else {
                calcHeight = undefined;
            }
        }

        if (needed) {
            for (i = 0; i < length; ++i) {
                childContext = childItems[i];
                child = childContext.target;
                height = calcHeight && childContext.getProp('height');
                width = calcWidth && childContext.getProp('width');
                margins = childContext.getMarginInfo();

                height += margins.bottom;
                width  += margins.right;

                contentHeight = Math.max(contentHeight, (child.y || 0) + height);
                contentWidth = Math.max(contentWidth, (child.x || 0) + width);

                if (isNaN(contentHeight) && isNaN(contentWidth)) {
                    me.done = false;
                    return;
                }
            }

            if (calcWidth || calcHeight) {
                targetPadding = ownerContext.targetContext.getPaddingInfo();
            }
            if (calcWidth && !ownerContext.setContentWidth(contentWidth + targetPadding.width)) {
                me.done = false;
            }
            if (calcHeight && !ownerContext.setContentHeight(contentHeight + targetPadding.height)) {
                me.done = false;
            }

            
        }
    }
});


Ext.define('Ext.layout.container.Accordion', {
    extend:  Ext.layout.container.VBox ,
    alias: ['layout.accordion'],
    alternateClassName: 'Ext.layout.AccordionLayout',

    targetCls: Ext.baseCSSPrefix + 'accordion-layout-ct',
    itemCls: [Ext.baseCSSPrefix + 'box-item', Ext.baseCSSPrefix + 'accordion-item'],

    align: 'stretch',

    
    fill : true,

    

    
    titleCollapse : true,

    
    hideCollapseTool : false,

    
    collapseFirst : undefined,

    
    animate : true,
    
    activeOnTop : false,
    
    multi: false,
    
    defaultAnimatePolicy: {
        y: true,
        height: true
    },

    constructor: function() {
        var me = this;

        me.callParent(arguments);

        if (!me.multi && me.animate) {
            me.animatePolicy = Ext.apply({}, me.defaultAnimatePolicy);
        } else {
            me.animatePolicy = null;
        }
    },

    beforeRenderItems: function (items) {
        var me = this,
            ln = items.length,
            i = 0,
            owner = me.owner,
            collapseFirst = me.collapseFirst,
            hasCollapseFirst = Ext.isDefined(collapseFirst),
            expandedItem = me.getExpanded(true)[0],
            multi = me.multi,
            comp;

        for (; i < ln; i++) {
            comp = items[i];
            if (!comp.rendered) {
                
                if (!multi || comp.collapsible !== false) {
                    comp.collapsible = true;
                }
                
                if (comp.collapsible) {
                    if (hasCollapseFirst) {
                        comp.collapseFirst = collapseFirst;
                    }
                    if (me.hideCollapseTool) {
                        comp.hideCollapseTool = me.hideCollapseTool;
                        comp.titleCollapse = true;
                    } else if (me.titleCollapse && comp.titleCollapse === undefined) {
                        
                        
                        comp.titleCollapse = me.titleCollapse;
                    }
                }
                
                delete comp.hideHeader;
                delete comp.width;
                comp.title = comp.title || '&#160;';
                comp.addBodyCls(Ext.baseCSSPrefix + 'accordion-body');

                
                
                
                if (!multi) {
                    if (expandedItem) {
                        comp.collapsed = expandedItem !== comp;
                    } else if (comp.hasOwnProperty('collapsed') && comp.collapsed === false) {
                        expandedItem = comp;
                    } else {
                        comp.collapsed = true;
                    }

                    
                    owner.mon(comp, 'show', me.onComponentShow, me);
                }
                
                
                comp.headerOverCls = Ext.baseCSSPrefix + 'accordion-hd-over';
            }
        }

        
        
        if (!me.processing && !multi) {
            if (!expandedItem) {
                if (ln) {
                    items[0].collapsed = false;
                }
            } else if (me.activeOnTop) {
                expandedItem.collapsed = false;
                me.configureItem(expandedItem);
                if (owner.items.indexOf(expandedItem) > 0) {
                    owner.insert(0, expandedItem);
                }
            }
        }
    },

    getItemsRenderTree: function(items) {
        this.beforeRenderItems(items);
        return this.callParent(arguments);
    },

    renderItems : function(items, target) {
        this.beforeRenderItems(items);

        this.callParent(arguments);
    },

    configureItem: function(item) {
        this.callParent(arguments);

        
        
        item.animCollapse = item.border = false;

        
        if (this.fill) {
            item.flex = 1;
        }
    },

    beginLayout: function (ownerContext) {
        this.callParent(arguments);
        this.updatePanelClasses(ownerContext);
    },

    updatePanelClasses: function(ownerContext) {
        var children = ownerContext.visibleItems,
            ln = children.length,
            siblingCollapsed = true,
            i, child, header;

        for (i = 0; i < ln; i++) {
            child = children[i];
            header = child.header;
            header.addCls(Ext.baseCSSPrefix + 'accordion-hd');

            if (siblingCollapsed) {
                header.removeCls(Ext.baseCSSPrefix + 'accordion-hd-sibling-expanded');
            } else {
                header.addCls(Ext.baseCSSPrefix + 'accordion-hd-sibling-expanded');
            }

            if (i + 1 == ln && child.collapsed) {
                header.addCls(Ext.baseCSSPrefix + 'accordion-hd-last-collapsed');
            } else {
                header.removeCls(Ext.baseCSSPrefix + 'accordion-hd-last-collapsed');
            }

            siblingCollapsed = child.collapsed;
        }
    },

    
    
    
    onBeforeComponentExpand: function(toExpand) {
        var me = this,
            owner = me.owner,
            multi = me.multi,
            animate = me.animate,
            moveToTop = !multi && !me.animate && me.activeOnTop,
            expanded,
            previousValue;

        if (!me.processing) {
            me.processing = true;
            previousValue = owner.deferLayouts;
            owner.deferLayouts = true;
            
            if (!multi) {
                expanded = me.getExpanded()[0];
                if (expanded && expanded !== toExpand) {
                    expanded.collapse();
                }
            }
            
            if (moveToTop) {
                
                Ext.suspendLayouts();
                owner.insert(0, toExpand);
                Ext.resumeLayouts();
            }
            
            owner.deferLayouts = previousValue;
            me.processing = false;
        }
    },

    onBeforeComponentCollapse: function(comp) {
        var me = this,
            owner = me.owner,
            toExpand,
            expanded,
            previousValue;

        if (me.owner.items.getCount() === 1) {
            
            return false;
        }

        if (!me.processing) {
            me.processing = true;
            previousValue = owner.deferLayouts;
            owner.deferLayouts = true;
            toExpand = comp.next() || comp.prev();

            
            
            if (me.multi) {
                expanded = me.getExpanded();

                
                
                if (expanded.length === 1) {
                    toExpand.expand();
                }

            } else if (toExpand) {
                toExpand.expand();
            }
            owner.deferLayouts = previousValue;
            me.processing = false;
        }
    },

    onComponentShow: function(comp) {
        this.onBeforeComponentExpand(comp);
    },

    onRemove: function(panel, isDestroying){
        var me = this,
            item;
            
        me.callParent(arguments);
        
        if (!me.owner.destroying && !me.multi && !panel.collapsed) {
            item = me.owner.items.first();
            if (item) {
                item.expand();
            }
        }
    },
    
    getExpanded: function(explicitCheck){
        var items = this.owner.items.items,
            len = items.length,
            i = 0,
            out = [],
            add,
            item;
            
        for (; i < len; ++i) {
            item = items[i];

            if (!item.hidden) {
                if (explicitCheck) {
                    add = item.hasOwnProperty('collapsed') && item.collapsed === false;
                } else {
                    add = !item.collapsed;
                }
                if (add) {
                    out.push(item);
                }
            }
        }
        return out;
            
    }
});


Ext.define('Ext.resizer.Splitter', {
    extend:  Ext.Component ,
                                
                                          
    alias: 'widget.splitter',

    childEls: [
        'collapseEl'
    ],

    renderTpl: [
        '<tpl if="collapsible===true">',
            '<div id="{id}-collapseEl" role="presentation" class="', Ext.baseCSSPrefix, 'collapse-el ',
                Ext.baseCSSPrefix, 'layout-split-{collapseDir}{childElCls}">&#160;',
            '</div>',
        '</tpl>'
    ],

    baseCls: Ext.baseCSSPrefix + 'splitter',
    collapsedClsInternal: Ext.baseCSSPrefix + 'splitter-collapsed',
    
    
    canResize: true,

    
    collapsible: false,

    

    
    collapseOnDblClick: true,

    
    defaultSplitMin: 40,

    
    defaultSplitMax: 1000,

    

    
    collapseTarget: 'next',

    

    horizontal: false,
    vertical: false,

    
    size: 5,
    
    ariaRole: 'separator',

    
    getTrackerConfig: function () {
        return {
            xclass: 'Ext.resizer.SplitterTracker',
            el: this.el,
            splitter: this
        };
    },

    beforeRender: function() {
        var me = this,
            target = me.getCollapseTarget();

        me.callParent();

        if (target.collapsed) {
            me.addCls(me.collapsedClsInternal);
        }
        if (!me.canResize) {
            me.addCls(me.baseCls + '-noresize');
        }

        Ext.applyIf(me.renderData, {
            collapseDir: me.getCollapseDirection(),
            collapsible: me.collapsible || target.collapsible
        });

        me.protoEl.unselectable();
    },

    onRender: function() {
        var me = this,
            collapseEl;

        me.callParent(arguments);

        
        if (me.performCollapse !== false) {
            if (me.renderData.collapsible) {
                me.mon(me.collapseEl, 'click', me.toggleTargetCmp, me);
            }
            if (me.collapseOnDblClick) {
                me.mon(me.el, 'dblclick', me.toggleTargetCmp, me);
            }
        }

        
        me.mon(me.getCollapseTarget(), {
            collapse: me.onTargetCollapse,
            expand: me.onTargetExpand,
            beforeexpand: me.onBeforeTargetExpand,
            beforecollapse: me.onBeforeTargetCollapse,
            scope: me
        });

        if (me.canResize) {
            me.tracker = Ext.create(me.getTrackerConfig());
            
            me.relayEvents(me.tracker, [ 'beforedragstart', 'dragstart', 'dragend' ]);
        }

        collapseEl = me.collapseEl;
        if (collapseEl) {
            collapseEl.lastCollapseDirCls = me.collapseDirProps[me.collapseDirection].cls;
        }
    },

    getCollapseDirection: function() {
        var me = this,
            dir = me.collapseDirection,
            collapseTarget, idx, items, type;

        if (!dir) {
            collapseTarget = me.collapseTarget;
            if (collapseTarget.isComponent) {
                dir = collapseTarget.collapseDirection;
            }

            if (!dir) {
                
                
                
                
                
                
                
                type = me.ownerCt.layout.type;
                if (collapseTarget.isComponent) {
                    items = me.ownerCt.items;
                    idx = Number(items.indexOf(collapseTarget) === items.indexOf(me) - 1) << 1 | Number(type === 'hbox');
                } else {
                    idx = Number(me.collapseTarget === 'prev') << 1 | Number(type === 'hbox');
                }

                
                dir = ['bottom', 'right', 'top', 'left'][idx];
            }

            me.collapseDirection = dir;
        }

        me.setOrientation((dir === 'top' || dir === 'bottom') ? 'horizontal' : 'vertical');

        return dir;
    },

    getCollapseTarget: function() {
        var me = this;

        return me.collapseTarget.isComponent ? me.collapseTarget
                    : me.collapseTarget === 'prev' ? me.previousSibling() : me.nextSibling();
    },
    
    setCollapseEl: function(display){
        var el = this.collapseEl;
        if (el) {
            el.setDisplayed(display);
        }
    },
    
    onBeforeTargetExpand: function(target) {
        this.setCollapseEl('none');
    },
    
    onBeforeTargetCollapse: function(){
        this.setCollapseEl('none');
    },

    onTargetCollapse: function(target) {
        this.el.addCls([this.collapsedClsInternal, this.collapsedCls]);
        this.setCollapseEl('');
    },

    onTargetExpand: function(target) {
        this.el.removeCls([this.collapsedClsInternal, this.collapsedCls]);
        this.setCollapseEl('');
    },

    collapseDirProps: {
        top: {
            cls: Ext.baseCSSPrefix + 'layout-split-top'
        },
        right: {
            cls: Ext.baseCSSPrefix + 'layout-split-right'
        },
        bottom: {
            cls: Ext.baseCSSPrefix + 'layout-split-bottom'
        },
        left: {
            cls: Ext.baseCSSPrefix + 'layout-split-left'
        }
    },

    orientationProps: {
        horizontal: {
            opposite: 'vertical',
            fixedAxis: 'height',
            stretchedAxis: 'width'
        },
        vertical: {
            opposite: 'horizontal',
            fixedAxis: 'width',
            stretchedAxis: 'height'
        }
    },

    applyCollapseDirection: function () {
        var me = this,
            collapseEl = me.collapseEl,
            collapseDirProps = me.collapseDirProps[me.collapseDirection],
            cls;

        if (collapseEl) {
            cls = collapseEl.lastCollapseDirCls;
            if (cls) {
                collapseEl.removeCls(cls);
            }

            collapseEl.addCls(collapseEl.lastCollapseDirCls = collapseDirProps.cls);
        }
    },

    applyOrientation: function () {
        var me = this,
            orientation = me.orientation,
            orientationProps = me.orientationProps[orientation],
            defaultSize = me.size,
            fixedSizeProp = orientationProps.fixedAxis,
            stretchSizeProp = orientationProps.stretchedAxis,
            cls = me.baseCls + '-';

        me[orientation] = true;
        me[orientationProps.opposite] = false;

        if (!me.hasOwnProperty(fixedSizeProp) || me[fixedSizeProp] === '100%') {
            me[fixedSizeProp] = defaultSize;
        }
        if (!me.hasOwnProperty(stretchSizeProp) || me[stretchSizeProp] === defaultSize) {
            me[stretchSizeProp] = '100%';
        }

        me.removeCls(cls + orientationProps.opposite);
        me.addCls(cls + orientation);
    },

    setOrientation: function (orientation) {
        var me = this;

        if (me.orientation !== orientation) {
            me.orientation = orientation;
            me.applyOrientation();
        }
    },
    
    updateOrientation: function () {
        delete this.collapseDirection; 
        this.getCollapseDirection();
        this.applyCollapseDirection();
    },

    toggleTargetCmp: function(e, t) {
        var cmp = this.getCollapseTarget(),
            placeholder = cmp.placeholder,
            toggle;

        
        if (Ext.isFunction(cmp.expand) && Ext.isFunction(cmp.collapse)) {
            if (placeholder && !placeholder.hidden) {
                toggle = true;
            } else {
                toggle = !cmp.hidden;
            }

            if (toggle) {
                if (cmp.collapsed) {
                    cmp.expand();
                } else if (cmp.collapseDirection) {
                    cmp.collapse();
                } else {
                    cmp.collapse(this.renderData.collapseDir);
                }
            }
        }
    },

    
    setSize: function() {
        var me = this;
        me.callParent(arguments);
        if (Ext.isIE && me.el) {
            me.el.repaint();
        }
    },
    
    beforeDestroy: function(){
        Ext.destroy(this.tracker);
        this.callParent();
    }
});


Ext.define('Ext.resizer.BorderSplitter', {
    extend:  Ext.resizer.Splitter ,

                                                

    alias: 'widget.bordersplitter',

    
    collapseTarget: null,

    getTrackerConfig: function () {
        var trackerConfig = this.callParent();

        trackerConfig.xclass = 'Ext.resizer.BorderSplitterTracker';

        return trackerConfig;
    }
});


Ext.define('Ext.layout.container.Border', {

    extend:  Ext.layout.container.Container ,
    alias: 'layout.border',
    alternateClassName: 'Ext.layout.BorderLayout',

               
                                     
                      

                                                                  
                                            
      


    targetCls: Ext.baseCSSPrefix + 'border-layout-ct',

    itemCls: [Ext.baseCSSPrefix + 'border-item', Ext.baseCSSPrefix + 'box-item'],

    type: 'border',

    isBorderLayout: true,

    
    
    

    
    padding: undefined,

    percentageRe: /(\d+)%/,
    
    horzPositionProp: 'left',
    padOnContainerProp: 'left',
    padNotOnContainerProp: 'right',

    
    axisProps: {
        horz: {
            borderBegin: 'west',
            borderEnd: 'east',
            horizontal: true,
            posProp: 'x',
            sizeProp: 'width',
            sizePropCap: 'Width'
        },
        vert: {
            borderBegin: 'north',
            borderEnd: 'south',
            horizontal: false,
            posProp: 'y',
            sizeProp: 'height',
            sizePropCap: 'Height'
        }
    },

    
    centerRegion: null,

    manageMargins: true,

    panelCollapseAnimate: true,

    panelCollapseMode: 'placeholder',

    
    regionWeights: {
        north: 20,
        south: 10,
        center: 0,
        west: -10,
        east: -20
    },

    
    

    
    beginAxis: function (ownerContext, regions, name) {
        var me = this,
            props = me.axisProps[name],
            isVert = !props.horizontal,
            sizeProp = props.sizeProp,
            totalFlex = 0,
            childItems = ownerContext.childItems,
            length = childItems.length,
            center, i, childContext, centerFlex, comp, region, match, size, type, target, placeholder;

        for (i = 0; i < length; ++i) {
            childContext = childItems[i];
            comp = childContext.target;

            childContext.layoutPos = {};

            if (comp.region) {
                childContext.region = region = comp.region;

                childContext.isCenter = comp.isCenter;
                childContext.isHorz = comp.isHorz;
                childContext.isVert = comp.isVert;

                childContext.weight = comp.weight || me.regionWeights[region] || 0;
                regions[comp.id] = childContext;

                if (comp.isCenter) {
                    center = childContext;
                    centerFlex = comp.flex;
                    ownerContext.centerRegion = center;

                    continue;
                }

                if (isVert !== childContext.isVert) {
                    continue;
                }

                

                childContext.reverseWeighting = (region == props.borderEnd);

                size = comp[sizeProp];
                type = typeof size;

                if (!comp.collapsed) {
                    if (type == 'string' && (match = me.percentageRe.exec(size))) {
                        childContext.percentage = parseInt(match[1], 10);
                    } else if (comp.flex) {
                        totalFlex += childContext.flex = comp.flex;
                    }
                }
            }
        }

        
        if (center) {
            target = center.target;

            if ((placeholder = target.placeholderFor)) {
                if (!centerFlex && isVert === placeholder.collapsedVertical()) {
                    
                    centerFlex = 0;
                    center.collapseAxis = name;
                }
            } else if (target.collapsed && (isVert === target.collapsedVertical())) {
                
                centerFlex = 0;
                center.collapseAxis = name;
            }
        }

        if (centerFlex == null) {
            
            centerFlex = 1;
        }

        totalFlex += centerFlex;

        return Ext.apply({
            before         : isVert ? 'top' : 'left',
            totalFlex      : totalFlex
        }, props);
    },

    beginLayout: function (ownerContext) {
        var me = this,
            items = me.getLayoutItems(),
            pad = me.padding,
            type = typeof pad,
            padOnContainer = false,
            childContext, item, length, i, regions, collapseTarget,
            doShow, hidden, region;

        
        if (pad) {
            if (type == 'string' || type == 'number') {
                pad = Ext.util.Format.parseBox(pad);
            }
        } else {
            pad = ownerContext.getEl('getTargetEl').getPaddingInfo();
            padOnContainer = true;
        }
        ownerContext.outerPad = pad;
        ownerContext.padOnContainer = padOnContainer;

        for (i = 0, length = items.length; i < length; ++i) {
            item = items[i];
            collapseTarget = me.getSplitterTarget(item);
            if (collapseTarget) {  
                doShow = undefined;
                hidden = !!item.hidden;
                if (!collapseTarget.split) {
                    if (collapseTarget.isCollapsingOrExpanding) {
                        doShow = !!collapseTarget.collapsed;
                    }
                } else if (hidden !== collapseTarget.hidden) {
                    doShow = !collapseTarget.hidden;
                }

                if (doShow) {
                    item.show();
                } else if (doShow === false) {
                    item.hide();
                }
            }
        }

        
        
        
        me.callParent(arguments);

        items = ownerContext.childItems;
        length = items.length;
        regions = {};

        ownerContext.borderAxisHorz = me.beginAxis(ownerContext, regions, 'horz');
        ownerContext.borderAxisVert = me.beginAxis(ownerContext, regions, 'vert');

        
        
        
        for (i = 0; i < length; ++i) {
            childContext = items[i];
            collapseTarget = me.getSplitterTarget(childContext.target);

            if (collapseTarget) { 
                region = regions[collapseTarget.id]
                if (!region) {
                        
                        
                        
                        region = ownerContext.getEl(collapseTarget.el, me);
                        region.region = collapseTarget.region;
                }
                childContext.collapseTarget = collapseTarget = region;
                childContext.weight = collapseTarget.weight;
                childContext.reverseWeighting = collapseTarget.reverseWeighting;
                collapseTarget.splitter = childContext;
                childContext.isHorz = collapseTarget.isHorz;
                childContext.isVert = collapseTarget.isVert;
            }
        }

        
        me.sortWeightedItems(items, 'reverseWeighting');
        me.setupSplitterNeighbors(items);
    },

    calculate: function (ownerContext) {
        var me = this,
            containerSize = me.getContainerSize(ownerContext),
            childItems = ownerContext.childItems,
            length = childItems.length,
            horz = ownerContext.borderAxisHorz,
            vert = ownerContext.borderAxisVert,
            pad = ownerContext.outerPad,
            padOnContainer = ownerContext.padOnContainer,
            i, childContext, childMargins, size, horzPercentTotal, vertPercentTotal;

        horz.begin = pad[me.padOnContainerProp];
        vert.begin = pad.top;
        
        
        
        horzPercentTotal = horz.end = horz.flexSpace = containerSize.width + (padOnContainer ? pad[me.padOnContainerProp] : -pad[me.padNotOnContainerProp]);
        vertPercentTotal = vert.end = vert.flexSpace = containerSize.height + (padOnContainer ? pad.top : -pad.bottom);

        
        
        for (i = 0; i < length; ++i) {
            childContext = childItems[i];
            childMargins = childContext.getMarginInfo();

            
            if (childContext.isHorz || childContext.isCenter) {
                horz.addUnflexed(childMargins.width);
                horzPercentTotal -= childMargins.width;
            }

            if (childContext.isVert || childContext.isCenter) {
                vert.addUnflexed(childMargins.height);
                vertPercentTotal -= childMargins.height;
            }

            
            if (!childContext.flex && !childContext.percentage) {
                if (childContext.isHorz || (childContext.isCenter && childContext.collapseAxis === 'horz')) {
                    size = childContext.getProp('width');

                    horz.addUnflexed(size);

                    
                    if (childContext.collapseTarget) {
                        horzPercentTotal -= size;
                    }
                } else if (childContext.isVert || (childContext.isCenter && childContext.collapseAxis === 'vert')) {
                    size = childContext.getProp('height');

                    vert.addUnflexed(size);

                    
                    if (childContext.collapseTarget) {
                        vertPercentTotal -= size;
                    }
                }
                
            }
        }

        for (i = 0; i < length; ++i) {
            childContext = childItems[i];
            childMargins = childContext.getMarginInfo();

            
            if (childContext.percentage) {
                if (childContext.isHorz) {
                    size = Math.ceil(horzPercentTotal * childContext.percentage / 100);
                    size = childContext.setWidth(size);
                    horz.addUnflexed(size);
                } else if (childContext.isVert) {
                    size = Math.ceil(vertPercentTotal * childContext.percentage / 100);
                    size = childContext.setHeight(size);
                    vert.addUnflexed(size);
                }
                
            }
        }


        
        

        for (i = 0; i < length; ++i) {
            childContext = childItems[i];

            if (!childContext.isCenter) {
                me.calculateChildAxis(childContext, horz);
                me.calculateChildAxis(childContext, vert);
            }
        }

        
        
        
        if (me.finishAxis(ownerContext, vert) + me.finishAxis(ownerContext, horz) < 2) {
            me.done = false;
        } else {
            
            
            
            me.finishPositions(childItems);
        }
    },

    
    calculateChildAxis: function (childContext, axis) {
        var collapseTarget = childContext.collapseTarget,
            setSizeMethod = 'set' + axis.sizePropCap,
            sizeProp = axis.sizeProp,
            childMarginSize = childContext.getMarginInfo()[sizeProp],
            region, isBegin, flex, pos, size;

        if (collapseTarget) { 
            region = collapseTarget.region;
        } else {
            region = childContext.region;
            flex = childContext.flex;
        }

        isBegin = region == axis.borderBegin;

        if (!isBegin && region != axis.borderEnd) {
            
            
            childContext[setSizeMethod](axis.end - axis.begin - childMarginSize);
            pos = axis.begin;
        } else {
            if (flex) {
                size = Math.ceil(axis.flexSpace * (flex / axis.totalFlex));
                size = childContext[setSizeMethod](size);
            } else if (childContext.percentage) {
                
                size = childContext.peek(sizeProp);
            } else {
                size = childContext.getProp(sizeProp);
            }

            size += childMarginSize;

            if (isBegin) {
                pos = axis.begin;
                axis.begin += size;
            } else {
                axis.end = pos = axis.end - size;
            }
        }

        childContext.layoutPos[axis.posProp] = pos;
    },

    
    finishAxis: function (ownerContext, axis) {
        var size = axis.end - axis.begin,
            center = ownerContext.centerRegion;

        if (center) {
            center['set' + axis.sizePropCap](size - center.getMarginInfo()[axis.sizeProp]);
            center.layoutPos[axis.posProp] = axis.begin;
        }

        return Ext.isNumber(size) ? 1 : 0;
    },

    
    finishPositions: function (childItems) {
        var length = childItems.length,
            index, childContext,
            marginProp = this.horzPositionProp;

        for (index = 0; index < length; ++index) {
            childContext = childItems[index];

            childContext.setProp('x', childContext.layoutPos.x + childContext.marginInfo[marginProp]);
            childContext.setProp('y', childContext.layoutPos.y + childContext.marginInfo.top);
        }
    },

    getLayoutItems: function() {
        var owner = this.owner,
            ownerItems = (owner && owner.items && owner.items.items) || [],
            length = ownerItems.length,
            items = [],
            i = 0,
            ownerItem, placeholderFor;

        for (; i < length; i++) {
            ownerItem = ownerItems[i];
            placeholderFor = ownerItem.placeholderFor;
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            if (ownerItem.hidden || ((!ownerItem.floated || ownerItem.isCollapsingOrExpanding === 2) &&
                !(placeholderFor && placeholderFor.isCollapsingOrExpanding === 2))) {
                items.push(ownerItem);
            } 
        }

        return items;
    },

    getPlaceholder: function (comp) {
        return comp.getPlaceholder && comp.getPlaceholder();
    },

    getSplitterTarget: function (splitter) {
        var collapseTarget = splitter.collapseTarget;

        if (collapseTarget && collapseTarget.collapsed) {
            return collapseTarget.placeholder || collapseTarget;
        }

        return collapseTarget;
    },

    isItemBoxParent: function (itemContext) {
        return true;
    },

    isItemShrinkWrap: function (item) {
        return true;
    },

    
    

    
    insertSplitter: function (item, index, hidden, splitterCfg) {
        var region = item.region,
            splitter = Ext.apply({
                xtype: 'bordersplitter',
                collapseTarget: item,
                id: item.id + '-splitter',
                hidden: hidden,
                canResize: item.splitterResize !== false,
                splitterFor: item
            }, splitterCfg),
            at = index + ((region === 'south' || region === 'east') ? 0 : 1);

        if (item.collapseMode === 'mini') {
            splitter.collapsedCls = item.collapsedCls;
        }

        item.splitter = this.owner.add(at, splitter);
    },

    
    onAdd: function (item, index) {
        var me = this,
            placeholderFor = item.placeholderFor,
            region = item.region,
            split,
            hidden,
            cfg;

        me.callParent(arguments);

        if (region) {
            Ext.apply(item, me.regionFlags[region]);

            if (item.initBorderRegion) {
                
                
                item.initBorderRegion();
            }

            if (region === 'center') {
                me.centerRegion = item;
            } else {
                split = item.split;
                hidden = !!item.hidden;
                
                if (typeof split === 'object') {
                    cfg = split;
                    split = true;
                }
                
                if ((item.isHorz || item.isVert) && (split || item.collapseMode == 'mini')) {
                    me.insertSplitter(item, index, hidden || !split, cfg);
                }
            }

            if (!item.hasOwnProperty('collapseMode')) {
                item.collapseMode = me.panelCollapseMode;
            }

            if (!item.hasOwnProperty('animCollapse')) {
                if (item.collapseMode !== 'placeholder') {
                    
                    
                    item.animCollapse = false;
                } else {
                    item.animCollapse = me.panelCollapseAnimate;
                }
            }
        } else if (placeholderFor) {
            Ext.apply(item, me.regionFlags[placeholderFor.region]);
            item.region = placeholderFor.region;
            item.weight = placeholderFor.weight;
        }
    },

    onDestroy: function() {
        this.centerRegion = null;
        this.callParent();
    },

    onRemove: function (comp, isDestroying) {
        var me = this,
            region = comp.region,
            splitter = comp.splitter,
            owner = me.owner,
            destroying = owner.destroying,
            el;

        if (region) {
            if (comp.isCenter) {
                me.centerRegion = null;
            }

            delete comp.isCenter;
            delete comp.isHorz;
            delete comp.isVert;

            
            if (splitter && !owner.destroying) {
                owner.doRemove(splitter, true); 
            }
            delete comp.splitter;
        }

        me.callParent(arguments);
        
        if (!destroying && !isDestroying && comp.rendered) {
            
            el = comp.getEl();
            el.setStyle('top', '');
            el.setStyle(me.horzPositionProp, '');
        }
    },

    
    

    regionMeta: {
        center: { splitterDelta: 0 },

        north: { splitterDelta:  1 },
        south: { splitterDelta: -1 },

        west:  { splitterDelta:  1 },
        east:  { splitterDelta: -1 }
    },

    
    regionFlags: {
        center: { isCenter: true, isHorz: false, isVert: false },

        north: { isCenter: false, isHorz: false, isVert: true, collapseDirection: 'top' },
        south: { isCenter: false, isHorz: false, isVert: true, collapseDirection: 'bottom' },

        west: { isCenter: false, isHorz: true, isVert: false, collapseDirection: 'left' },
        east: { isCenter: false, isHorz: true, isVert: false, collapseDirection: 'right' }
    },

    setupSplitterNeighbors: function (items) {
        var edgeRegions = {
                
                
                
                
            },
            length = items.length,
            touchedRegions = this.touchedRegions,
            i, j, center, count, edge, comp, region, splitter, touched;

        for (i = 0; i < length; ++i) {
            comp = items[i].target;
            region = comp.region;

            if (comp.isCenter) {
                center = comp;
            } else if (region) {
                touched = touchedRegions[region];

                for (j = 0, count = touched.length; j < count; ++j) {
                    edge = edgeRegions[touched[j]];
                    if (edge) {
                        edge.neighbors.push(comp);
                    }
                }
                
                if (comp.placeholderFor) {
                    
                    splitter = comp.placeholderFor.splitter;
                } else {
                    splitter = comp.splitter;
                }
                if (splitter) {
                    splitter.neighbors = [];
                }

                edgeRegions[region] = splitter;
            }
        }

        if (center) {
            touched = touchedRegions.center;

            for (j = 0, count = touched.length; j < count; ++j) {
                edge = edgeRegions[touched[j]];
                if (edge) {
                    edge.neighbors.push(center);
                }
            }
        }
    },

    
    touchedRegions: {
        center: [ 'north', 'south', 'east',  'west' ],

        north:  [ 'north', 'east',  'west'  ],
        south:  [ 'south', 'east',  'west'  ],
        east:   [ 'east',  'north', 'south' ],
        west:   [ 'west',  'north', 'south' ]
    },

    sizePolicies: {
        vert: {
            readsWidth: 0,
            readsHeight: 1,
            setsWidth: 1,
            setsHeight: 0
        },
        horz: {
            readsWidth: 1,
            readsHeight: 0,
            setsWidth: 0,
            setsHeight: 1
        },
        flexAll: {
            readsWidth: 0,
            readsHeight: 0,
            setsWidth: 1,
            setsHeight: 1
        }
    },

    getItemSizePolicy: function (item) {
        var me = this,
            policies = this.sizePolicies,
            collapseTarget, size, policy, placeholderFor;

        if (item.isCenter) {
            placeholderFor = item.placeholderFor;

            if (placeholderFor) {
                if (placeholderFor.collapsedVertical()) {
                    return policies.vert;
                }
                return policies.horz;
            }
            if (item.collapsed) {
                if (item.collapsedVertical()) {
                    return policies.vert;
                }
                return policies.horz;
            }
            return policies.flexAll;
        }

        collapseTarget = item.collapseTarget;

        if (collapseTarget) {
            return collapseTarget.isVert ? policies.vert : policies.horz;
        }

        if (item.region) {
            if (item.isVert) {
                size = item.height;
                policy = policies.vert;
            } else {
                size = item.width;
                policy = policies.horz;
            }

            if (item.flex || (typeof size == 'string' && me.percentageRe.test(size))) {
                return policies.flexAll;
            }

            return policy;
        }

        return me.autoSizePolicy;
    }
}, function () {
    var methods = {
        addUnflexed: function (px) {
            this.flexSpace = Math.max(this.flexSpace - px, 0);
        }
    },
    props = this.prototype.axisProps;

    Ext.apply(props.horz, methods);
    Ext.apply(props.vert, methods);
});


Ext.define('Ext.layout.container.Card', {

    

    extend:  Ext.layout.container.Fit ,

    alternateClassName: 'Ext.layout.CardLayout',

    alias: 'layout.card',

    

    type: 'card',

    hideInactive: true,

    
    deferredRender : false,
    
    getRenderTree: function () {
        var me = this,
            activeItem = me.getActiveItem();

        if (activeItem) {

            
            if (activeItem.hasListeners.beforeactivate && activeItem.fireEvent('beforeactivate', activeItem) === false) {
 
                
                
                
                activeItem = me.activeItem = me.owner.activeItem = null;
            }
            
            
            else if (activeItem.hasListeners.activate) {
                activeItem.on({
                    boxready: function() {
                        activeItem.fireEvent('activate', activeItem);
                    },
                    single: true
                });
            }

            if (me.deferredRender) {
                if (activeItem) {
                    return me.getItemsRenderTree([activeItem]);
                }
            } else {
                return me.callParent(arguments);
            }
        }
    },

    renderChildren: function () {
        var me = this,
            active = me.getActiveItem();
            
        if (!me.deferredRender) {
            me.callParent();
        } else if (active) {
            
            me.renderItems([active], me.getRenderTarget());
        }
    },

    isValidParent : function(item, target, position) {
        
        
        var itemEl = item.el ? item.el.dom : Ext.getDom(item);
        return (itemEl && itemEl.parentNode === (target.dom || target)) || false;
    },

    
    getActiveItem: function() {
        var me = this,
            
            result = me.parseActiveItem(me.activeItem || (me.owner && me.owner.activeItem));

        
        if (result && me.owner.items.indexOf(result) != -1) {
            me.activeItem = result;
        } else {
            me.activeItem = null;
        }

        return me.activeItem;
    },

    
    parseActiveItem: function(item) {
        if (item && item.isComponent) {
            return item;
        } else if (typeof item == 'number' || item === undefined) {
            return this.getLayoutItems()[item || 0];
        } else {
            return this.owner.getComponent(item);
        }
    },

    
    
    configureItem: function(item) {
        if (item === this.getActiveItem()) {
            item.hidden = false;
        } else {
            item.hidden = true;
        }
        this.callParent(arguments);
    },

    onRemove: function(component) {
        this.callParent(arguments);
        if (component === this.activeItem) {
            this.activeItem = null;
        }
    },

    
    getAnimation: function(newCard, owner) {
        var newAnim = (newCard || {}).cardSwitchAnimation;
        if (newAnim === false) {
            return false;
        }
        return newAnim || owner.cardSwitchAnimation;
    },

    
    getNext: function() {
        var wrap = arguments[0],
            items = this.getLayoutItems(),
            index = Ext.Array.indexOf(items, this.activeItem);
            
        return items[index + 1] || (wrap ? items[0] : false);
    },

    
    next: function() {
        var anim = arguments[0], 
            wrap = arguments[1];
        return this.setActiveItem(this.getNext(wrap), anim);
    },

    
    getPrev: function() {
        var wrap = arguments[0],
            items = this.getLayoutItems(),
            index = Ext.Array.indexOf(items, this.activeItem);
            
        return items[index - 1] || (wrap ? items[items.length - 1] : false);
    },

    
    prev: function() {
        var anim = arguments[0], 
            wrap = arguments[1];
        return this.setActiveItem(this.getPrev(wrap), anim);
    },

    
    setActiveItem: function(newCard) {
        var me = this,
            owner = me.owner,
            oldCard = me.activeItem,
            rendered = owner.rendered,
            newIndex;

        newCard = me.parseActiveItem(newCard);
        newIndex = owner.items.indexOf(newCard);

        
        
        if (newIndex == -1) {
            newIndex = owner.items.items.length;
            Ext.suspendLayouts();
            newCard = owner.add(newCard);
            Ext.resumeLayouts();
        }

        
        if (newCard && oldCard != newCard) {
            
            if (newCard.fireEvent('beforeactivate', newCard, oldCard) === false) {
                return false;
            }
            if (oldCard && oldCard.fireEvent('beforedeactivate', oldCard, newCard) === false) {
                return false;
            }

            if (rendered) {
                Ext.suspendLayouts();

                
                if (!newCard.rendered) {
                    me.renderItem(newCard, me.getRenderTarget(), owner.items.length);
                }

                if (oldCard) {
                    if (me.hideInactive) {
                        oldCard.hide();
                        oldCard.hiddenByLayout = true;
                    }
                    oldCard.fireEvent('deactivate', oldCard, newCard);
                }
                
                if (newCard.hidden) {
                    newCard.show();
                }

                
                if (!newCard.hidden) {
                    me.activeItem = newCard;
                }
                Ext.resumeLayouts(true);
            } else {
                me.activeItem = newCard;
            }

            newCard.fireEvent('activate', newCard, oldCard);

            return me.activeItem;
        }
        return false;
    }
});


Ext.define('Ext.layout.container.Column', {

    extend:  Ext.layout.container.Auto ,
    alias: ['layout.column'],
    alternateClassName: 'Ext.layout.ColumnLayout',

    type: 'column',

    itemCls: Ext.baseCSSPrefix + 'column',

    targetCls: Ext.baseCSSPrefix + 'column-layout-ct',

    
    columnWidthSizePolicy: {
        readsWidth: 0,
        readsHeight: 1,
        setsWidth: 1,
        setsHeight: 0
    },
    
    createsInnerCt: true,

    manageOverflow: true,
    
    isItemShrinkWrap: function(ownerContext){
        return true;
    },

    getItemSizePolicy: function (item, ownerSizeModel) {
        if (item.columnWidth) {
            if (!ownerSizeModel) {
                ownerSizeModel = this.owner.getSizeModel();
            }

            if (!ownerSizeModel.width.shrinkWrap) {
                return this.columnWidthSizePolicy;
            }
        }
        return this.autoSizePolicy;
    },

    calculateItems: function (ownerContext, containerSize) {
        var me = this,
            targetContext = ownerContext.targetContext,
            items = ownerContext.childItems,
            len = items.length,
            contentWidth = 0,
            gotWidth = containerSize.gotWidth,
            blocked, availableWidth, i, itemContext, itemMarginWidth, itemWidth;

        
        if (gotWidth === false) { 
            
            targetContext.domBlock(me, 'width');
            blocked = true;
        } else if (gotWidth) {
            availableWidth = containerSize.width;
        } else {
            
            
            return true;
        }

        
        
        for (i = 0; i < len; ++i) {
            itemContext = items[i];

            
            
            
            itemMarginWidth = itemContext.getMarginInfo().width;

            if (!itemContext.widthModel.calculated) {
                itemWidth = itemContext.getProp('width');
                if (typeof itemWidth != 'number') {
                    itemContext.block(me, 'width');
                    blocked = true;
                }

                contentWidth += itemWidth + itemMarginWidth;
            }
        }

        if (!blocked) {
            availableWidth = (availableWidth < contentWidth) ? 0 : availableWidth - contentWidth;

            for (i = 0; i < len; ++i) {
                itemContext = items[i];
                if (itemContext.widthModel.calculated) {
                    itemMarginWidth = itemContext.marginInfo.width; 
                    itemWidth = itemContext.target.columnWidth;
                    itemWidth = Math.floor(itemWidth * availableWidth) - itemMarginWidth;
                    itemWidth = itemContext.setWidth(itemWidth); 
                    contentWidth += itemWidth + itemMarginWidth;
                }
            }

            ownerContext.setContentWidth(contentWidth + ownerContext.paddingContext.getPaddingInfo().width);
        }

        
        return !blocked;
    },

    setCtSizeIfNeeded: function(ownerContext, containerSize) {
        var me = this,
            padding = ownerContext.paddingContext.getPaddingInfo();

        me.callParent(arguments);

        
        
        if ((Ext.isIEQuirks || Ext.isIE7m) && me.isShrinkWrapTpl && padding.right) {
            ownerContext.outerCtContext.setProp('width',
                containerSize.width + padding.left);
        }
    }

});


Ext.define('Ext.layout.container.Form', {

    

    alias: 'layout.form',
    extend:  Ext.layout.container.Container ,
    alternateClassName: 'Ext.layout.FormLayout',

    
   
    tableCls: Ext.baseCSSPrefix + 'form-layout-table',

    type: 'form',
    
    createsInnerCt: true,

    manageOverflow: true,

    
    lastOverflowAdjust: {
        width: 0,
        height: 0
    },

    childEls: ['formTable'],
    
    padRow: '<tr role="presentation"><td class="' + Ext.baseCSSPrefix + 'form-item-pad" colspan="3" role="presentation"></td></tr>',

    renderTpl: [
        '<table id="{ownerId}-formTable" class="{tableCls}" style="width:100%" cellpadding="0" role="presentation">',
            '{%this.renderBody(out,values)%}',
        '</table>',
        '{%this.renderPadder(out,values)%}'
    ],

    getRenderData: function(){
        var data = this.callParent();
        data.tableCls = this.tableCls;
        return data;    
    },

    calculate : function (ownerContext) {
        var me = this,
            containerSize = me.getContainerSize(ownerContext, true),
            tableWidth,
            childItems,
            i = 0, length,
            shrinkwrapHeight = ownerContext.sizeModel.height.shrinkWrap;

        if (shrinkwrapHeight) {
            if (ownerContext.hasDomProp('containerChildrenSizeDone')) {
                ownerContext.setProp('contentHeight', me.formTable.dom.offsetHeight + ownerContext.targetContext.getPaddingInfo().height);
            } else {
                me.done = false;
            }
        }

        
        if (containerSize.gotWidth) {
            tableWidth = me.formTable.dom.offsetWidth;
            childItems = ownerContext.childItems;

            for (length = childItems.length; i < length; ++i) {
                childItems[i].setWidth(tableWidth, false);
            }
        } else {
            me.done = false;
        }
    },

    getRenderTarget: function() {
        return this.formTable;
    },

    getRenderTree: function() {
        var me = this,
            result = me.callParent(arguments),
            i, len;

        for (i = 0, len = result.length; i < len; i++) {
            result[i] = me.transformItemRenderTree(result[i]);
        }
        return result;
    },

    transformItemRenderTree: function(item) {

        if (item.tag && item.tag == 'table') {
            item.tag = 'tbody';
            item.role = 'presentation';
            delete item.cellspacing;
            delete item.cellpadding;

            
            
            
            
            if (Ext.isIE6) {
                item.cn = this.padRow;
            }

            return item;
        }

        return {
            tag: 'tbody',
            role: 'presentation',
            cn: {
                tag: 'tr',
                role: 'presentation',
                cn: {
                    tag: 'td',
                    role: 'presentation',
                    colspan: 3,
                    style: 'width:100%',
                    cn: item
                }
            }
        };

    },

    isValidParent: function(item, target, position) {
        return true;
    },

    isItemShrinkWrap: function(item) {
        return ((item.shrinkWrap === true) ? 3 : item.shrinkWrap||0) & 2;
    },

    getItemSizePolicy: function(item) {
        return {
            setsWidth: 1,
            setsHeight: 0
        };
    },





    beginLayoutCycle: function (ownerContext, firstCycle) {
        var padEl = this.overflowPadderEl;

        if (padEl) {
            padEl.setStyle('display', 'none');
        }

        
        
        if (!ownerContext.state.overflowAdjust) {
            ownerContext.state.overflowAdjust = this.lastOverflowAdjust;
        }
    },

    
    calculateOverflow: function (ownerContext, containerSize, dimensions) {
        var me = this,
            targetContext = ownerContext.targetContext,
            manageOverflow = me.manageOverflow,
            state = ownerContext.state,
            overflowAdjust = state.overflowAdjust,
            padWidth, padHeight, padElContext, padding, scrollRangeFlags,
            scrollbarSize, contentW, contentH, ownerW, ownerH, scrollbars,
            xauto, yauto;

        if (manageOverflow && !state.secondPass && !me.reserveScrollbar) {
            
            
            xauto = (me.getOverflowXStyle(ownerContext) === 'auto');
            yauto = (me.getOverflowYStyle(ownerContext) === 'auto');

            
            
            
            if (!containerSize.gotWidth) {
                xauto = false;
            }
            if (!containerSize.gotHeight) {
                yauto = false;
            }

            if (xauto || yauto) {
                scrollbarSize = Ext.getScrollbarSize();

                
                
                contentW = ownerContext.peek('contentWidth');
                contentH = ownerContext.peek('contentHeight');
                
                
                
                
                padding = targetContext.getPaddingInfo();
                contentW -= padding.width;
                contentH -= padding.height;
                ownerW = containerSize.width;
                ownerH = containerSize.height;

                scrollbars = me.getScrollbarsNeeded(ownerW, ownerH, contentW, contentH);
                state.overflowState = scrollbars;

                if (typeof dimensions == 'number') {
                    scrollbars &= ~dimensions; 
                }

                overflowAdjust = {
                    width:  (xauto && (scrollbars & 2)) ? scrollbarSize.width : 0,
                    height: (yauto && (scrollbars & 1)) ? scrollbarSize.height : 0
                };

                
                
                if (overflowAdjust.width !== me.lastOverflowAdjust.width || overflowAdjust.height !== me.lastOverflowAdjust.height) {
                    me.done = false;

                    
                    
                    ownerContext.invalidate({
                        state: {
                            overflowAdjust: overflowAdjust,
                            overflowState: state.overflowState,
                            secondPass: true
                        }
                    });
                }
            }
        }

        if (!me.done) {
            return;
        }

        padElContext = ownerContext.padElContext ||
                      (ownerContext.padElContext = ownerContext.getEl('overflowPadderEl', me));

        
        
        if (padElContext) {
            scrollbars = state.overflowState; 
            padWidth = ownerContext.peek('contentWidth');
            
            
            padHeight = 1;

            if (scrollbars) {
                padding = targetContext.getPaddingInfo();
                scrollRangeFlags = me.scrollRangeFlags;

                if ((scrollbars & 2) && (scrollRangeFlags & 1)) { 
                    padHeight += padding.bottom;
                }

                if ((scrollbars & 1) && (scrollRangeFlags & 4)) { 
                    padWidth += padding.right;
                }
                padElContext.setProp('display', '');
                padElContext.setSize(padWidth, padHeight);
            } else {
                padElContext.setProp('display', 'none');
            }
        }
    },

    completeLayout: function (ownerContext) {
        
        this.lastOverflowAdjust = ownerContext.state.overflowAdjust;
    },

    
    doRenderPadder: function (out, renderData) {
        
        

        var me = renderData.$layout,
            owner = me.owner,
            scrollRangeFlags = me.getScrollRangeFlags();

        if (me.manageOverflow) {
            if (scrollRangeFlags & 5) { 
                out.push('<div id="',owner.id,'-overflowPadderEl" role="presentation"',
                    
                    
                    
                    
                    
                    'style="font-size: 1px; height: 1px; margin-top: -1px; position: relative; z-index: -99999');

                out.push('"></div>');

                me.scrollRangeFlags = scrollRangeFlags; 
            }
        }
    },

    
    getContainerSize : function(ownerContext, inDom, ignoreOverflow) {
        
        
        
        
        

        var targetContext = ownerContext.targetContext,
            frameInfo = targetContext.getFrameInfo(),
            
            padding = targetContext.getPaddingInfo(),
            got = 0,
            needed = 0,
            overflowAdjust = ignoreOverflow ?  null : ownerContext.state.overflowAdjust,
            gotWidth, gotHeight, width, height;

        
        
        

        
        
        

        if (!ownerContext.widthModel.shrinkWrap) {
            ++needed;
            width = inDom ? targetContext.getDomProp('width') : targetContext.getProp('width');
            gotWidth = (typeof width == 'number');
            if (gotWidth) {
                ++got;
                width -= frameInfo.width + padding.width;
                if (overflowAdjust) {
                    width -= overflowAdjust.width;
                }
            }
        }

        if (!ownerContext.heightModel.shrinkWrap) {
            ++needed;
            height = inDom ? targetContext.getDomProp('height') : targetContext.getProp('height');
            gotHeight = (typeof height == 'number');
            if (gotHeight) {
                ++got;
                height -= frameInfo.height + padding.height;
                if (overflowAdjust) {
                    height -= overflowAdjust.height;
                }
            }
        }

        return {
            width: width,
            height: height,
            needed: needed,
            got: got,
            gotAll: got == needed,
            gotWidth: gotWidth,
            gotHeight: gotHeight
        };
    },

    
    getOverflowXStyle: function(ownerContext) {
        var me = this;

        return me.overflowXStyle ||
            (me.overflowXStyle = me.owner.scrollFlags.overflowX || ownerContext.targetContext.getStyle('overflow-x'));
    },

    
    getOverflowYStyle: function(ownerContext) {
        var me = this;

        return me.overflowYStyle || 
            (me.overflowYStyle = me.owner.scrollFlags.overflowY || ownerContext.targetContext.getStyle('overflow-y'));
    },

    
    getScrollRangeFlags: (function () {
        var flags = -1;

        return function () {
            if (flags < 0) {
                var div = Ext.getBody().createChild({
                        
                        cls: Ext.baseCSSPrefix + 'border-box',
                        role: 'presentation',
                        style: {
                            width: '100px', height: '100px', padding: '10px',
                            overflow: 'auto'
                        },
                        children: [{
                            role: 'presentation',
                            style: {
                                border: '1px solid red',
                                width: '150px', height: '150px',
                                margin: '0 5px 5px 0' 
                            }
                        }]
                    }),
                    scrollHeight = div.dom.scrollHeight,
                    scrollWidth = div.dom.scrollWidth,
                    heightFlags = {
                        
                        175: 0,
                        
                        165: 1,
                        
                        170: 2,
                        
                        160: 3
                    },
                    widthFlags = {
                        
                        175: 0,
                        
                        165: 4,
                        
                        170: 8,
                        
                        160: 12
                    };

                flags = (heightFlags[scrollHeight] || 0) | (widthFlags[scrollWidth] || 0);
                
                div.remove();
            }

            return flags;
        };
    }()),

    initLayout: function() {
        var me = this,
            scrollbarWidth = Ext.getScrollbarSize().width;

        me.callParent();

        
        
        
        if (scrollbarWidth && me.manageOverflow && !me.hasOwnProperty('lastOverflowAdjust')) {
            if (me.owner.scrollFlags.y || me.reserveScrollbar) {
                me.lastOverflowAdjust = {
                    width: scrollbarWidth,
                    height: 0
                };
            }
        }
    },

    setupRenderTpl: function (renderTpl) {
        this.callParent(arguments);

        renderTpl.renderPadder = this.doRenderPadder;
    }
});


Ext.define('Ext.menu.Item', {
    extend:  Ext.Component ,
    alias: 'widget.menuitem',
    alternateClassName: 'Ext.menu.TextItem',

    
    isMenuItem: true,

    mixins: {
        queryable:  Ext.Queryable 
    },

    

    

    
    activeCls: Ext.baseCSSPrefix + 'menu-item-active',

    
    ariaRole: 'menuitem',

    
    canActivate: true,

    
    clickHideDelay: 0,

    
    destroyMenu: true,

    
    disabledCls: Ext.baseCSSPrefix + 'menu-item-disabled',

    

    

    
    hideOnClick: true,

    

    

    

    

    

    
    menuAlign: 'tl-tr?',

    
    menuExpandDelay: 200,

    
    menuHideDelay: 200,

    

    

    
    tooltipType: 'qtip',

    arrowCls: Ext.baseCSSPrefix + 'menu-item-arrow',
    baseIconCls: Ext.baseCSSPrefix + 'menu-item-icon',
    textCls: Ext.baseCSSPrefix + 'menu-item-text',
    indentCls: Ext.baseCSSPrefix + 'menu-item-indent',
    indentNoSeparatorCls: Ext.baseCSSPrefix + 'menu-item-indent-no-separator',
    indentRightIconCls: Ext.baseCSSPrefix + 'menu-item-indent-right-icon',
    indentRightArrowCls: Ext.baseCSSPrefix + 'menu-item-indent-right-arrow',
    linkCls: Ext.baseCSSPrefix + 'menu-item-link',

    childEls: [
        'itemEl', 'iconEl', 'textEl', 'arrowEl'
    ],

    renderTpl: [
        '<tpl if="plain">',
            '{text}',
        '<tpl else>',
            '<a id="{id}-itemEl"',
                ' class="{linkCls} {indentCls}{childElCls}"',
                ' href="{href}" role="presentation" ',
                '<tpl if="hrefTarget"> target="{hrefTarget}"</tpl>',
                ' hidefocus="true"',
                
                ' unselectable="on"',
                '<tpl if="tabIndex">',
                    ' tabIndex="{tabIndex}"',
                '</tpl>',
            '>',
                '<span id="{id}-textEl" class="{textCls}{childElCls}" unselectable="on">{text}</span>',
                '<tpl if="hasIcon">',
                    '<div role="presentation" id="{id}-iconEl" class="{baseIconCls}',
                        '{[values.rightIcon ? "-right" : ""]} {iconCls}',
                        '{childElCls} {glyphCls}" style="<tpl if="icon">background-image:url({icon});</tpl>',
                        '<tpl if="glyph && glyphFontFamily">font-family:{glyphFontFamily};</tpl>">',
                        '<tpl if="glyph">&#{glyph};</tpl>',
                    '</div>',
                '</tpl>',
                '<tpl if="showCheckbox">',
                    '<div role="presentation" id="{id}-checkEl" class="{baseIconCls}',
                        '{[(values.hasIcon && !values.rightIcon) ? "-right" : ""]} ',
                        '{groupCls} {checkboxCls}{childElCls}">',
                    '</div>',
                '</tpl>',
                '<tpl if="hasMenu">',
                    '<div role="presentation" id="{id}-arrowEl" class="{arrowCls}{childElCls}"></div>',
                '</tpl>',
            '</a>',
        '</tpl>'
    ],

    maskOnDisable: false,

    iconAlign: 'left',

    

    

    activate: function(skipCheck) {
        var me = this;

        if (skipCheck || (!me.activated && me.canActivate && me.rendered && !me.isDisabled() && me.isVisible())) {
            if (!me.plain) {
                me.el.addCls(me.activeCls);
            }

            
            
            me.focus(false, true);
            me.activated = true;
            if (me.hasListeners.activate) {
                me.fireEvent('activate', me);
            }
        }
    },

    getFocusEl: function() {
        return this.itemEl;
    },

    deactivate: function() {
        var me = this,
            parent;

        if (me.activated) {
            parent = me.up('');
            if (!me.plain) {
                me.el.removeCls(me.activeCls);
            }

            
            
            if (parent) {
                parent.focus(false, true);
            }
            me.hideMenu();
            me.activated = false;
            if (me.hasListeners.deactivate) {
                me.fireEvent('deactivate', me);
            }
        }
    },

    deferHideMenu: function() {
        if (this.menu.isVisible()) {
            this.menu.hide();
        }
    },
    
    cancelDeferHide: function(){
        clearTimeout(this.hideMenuTimer);
    },

    deferHideParentMenus: function() {
        var ancestor;
        Ext.menu.Manager.hideAll();

        if (!Ext.Element.getActiveElement()) {
            
            ancestor = this.up(':not([hidden])');
            if (ancestor) {
                ancestor.focus();
            }
        }
    },

    expandMenu: function(delay) {
        var me = this;

        if (me.menu) {
            me.cancelDeferHide();
            if (delay === 0) {
                me.doExpandMenu();
            } else {
                clearTimeout(me.expandMenuTimer);
                me.expandMenuTimer = Ext.defer(me.doExpandMenu, Ext.isNumber(delay) ? delay : me.menuExpandDelay, me);
            }
        }
    },

    doExpandMenu: function() {
        var me = this,
            menu = me.menu;

        if (me.activated && (!menu.rendered || !menu.isVisible())) {
            me.parentMenu.activeChild = menu;
            menu.parentItem = me;
            menu.parentMenu = me.parentMenu;
            menu.showBy(me, me.menuAlign);
        }
    },

    getRefItems: function(deep) {
        var menu = this.menu,
            items;

        if (menu) {
            items = menu.getRefItems(deep);
            items.unshift(menu);
        }
        return items || [];
    },

    hideMenu: function(delay) {
        var me = this;

        if (me.menu) {
            clearTimeout(me.expandMenuTimer);
            me.hideMenuTimer = Ext.defer(me.deferHideMenu, Ext.isNumber(delay) ? delay : me.menuHideDelay, me);
        }
    },

    initComponent: function() {
        var me = this,
            prefix = Ext.baseCSSPrefix,
            cls = [prefix + 'menu-item'],
            menu;

        me.addEvents(
            
            'activate',

            
            'click',

            
            'deactivate',

            
            'textchange',

            
            'iconchange'
        );

        if (me.plain) {
            cls.push(prefix + 'menu-item-plain');
        }

        if (me.cls) {
            cls.push(me.cls);
        }

        me.cls = cls.join(' ');

        if (me.menu) {
            menu = me.menu;
            delete me.menu;
            me.setMenu(menu);
        }

        me.callParent(arguments);
    },

    onClick: function(e) {
        var me = this,
            clickHideDelay = me.clickHideDelay;

        if (!me.href) {
            e.stopEvent();
        }

        if (me.disabled) {
            return;
        }

        if (me.hideOnClick) {
            if (!clickHideDelay) {
                me.deferHideParentMenus();
            } else {
                me.deferHideParentMenusTimer = Ext.defer(me.deferHideParentMenus, clickHideDelay, me);
            }
        }

        Ext.callback(me.handler, me.scope || me, [me, e]);
        me.fireEvent('click', me, e);

        if (!me.hideOnClick) {
            me.focus();
        }
    },

    onRemoved: function() {
        var me = this;

        
        if (me.activated && me.parentMenu.activeItem === me) {
            me.parentMenu.deactivateActiveItem();
        }
        me.callParent(arguments);
        me.parentMenu = me.ownerButton = null;
    },

    
    beforeDestroy: function() {
        var me = this;
        if (me.rendered) {
            me.clearTip();
        }
        me.callParent();
    },

    onDestroy: function() {
        var me = this;

        clearTimeout(me.expandMenuTimer);
        me.cancelDeferHide();
        clearTimeout(me.deferHideParentMenusTimer);

        me.setMenu(null);
        me.callParent(arguments);
    },

    beforeRender: function() {
        var me = this,
            glyph = me.glyph,
            glyphFontFamily = Ext._glyphFontFamily,
            hasIcon = !!(me.icon || me.iconCls || glyph),
            hasMenu = !!me.menu,
            rightIcon = ((me.iconAlign === 'right') && !hasMenu),
            isCheckItem = me.isMenuCheckItem,
            indentCls = [],
            ownerCt = me.ownerCt,
            isOwnerPlain = ownerCt.plain,
            glyphParts;

        me.callParent();

        if (hasIcon) {
            if (hasMenu && me.showCheckbox) {
                
                
                hasIcon = false;
            }
        }

        if (typeof glyph === 'string') {
            glyphParts = glyph.split('@');
            glyph = glyphParts[0];
            glyphFontFamily = glyphParts[1];
        }

        if (!isOwnerPlain || (hasIcon && !rightIcon) || isCheckItem) {
            if (ownerCt.showSeparator && !isOwnerPlain) {
                indentCls.push(me.indentCls);
            } else {
                indentCls.push(me.indentNoSeparatorCls);
            }
        }

        if (hasMenu) {
            indentCls.push(me.indentRightArrowCls);
        } else if (hasIcon && (rightIcon || isCheckItem)) {
            indentCls.push(me.indentRightIconCls);
        }

        Ext.applyIf(me.renderData, {
            href: me.href || '#',
            hrefTarget: me.hrefTarget,
            icon: me.icon,
            iconCls: me.iconCls,
            glyph: glyph,
            glyphCls: glyph ? Ext.baseCSSPrefix + 'menu-item-glyph' : undefined,
            glyphFontFamily: glyphFontFamily,
            hasIcon: hasIcon,
            hasMenu: hasMenu,
            indent: !isOwnerPlain || hasIcon || isCheckItem,
            isCheckItem: isCheckItem,
            rightIcon: rightIcon,
            plain: me.plain,
            text: me.text,
            arrowCls: me.arrowCls,
            baseIconCls: me.baseIconCls,
            textCls: me.textCls,
            indentCls: indentCls.join(' '),
            linkCls: me.linkCls,
            groupCls: me.group ? me.groupCls : '',
            tabIndex: me.tabIndex
        });
    },

    onRender: function() {
        var me = this;

        me.callParent(arguments);

        if (me.tooltip) {
            me.setTooltip(me.tooltip, true);
        }
    },
    
    
    setMenu: function(menu, destroyMenu) {
        var me = this,
            oldMenu = me.menu,
            arrowEl = me.arrowEl;
            
        if (oldMenu) {
            delete oldMenu.parentItem;
            delete oldMenu.parentMenu;
            delete oldMenu.ownerItem;
            
            if (destroyMenu === true || (destroyMenu !== false && me.destroyMenu)) {
                Ext.destroy(oldMenu);
            }
        }
        if (menu) {
            me.menu = Ext.menu.Manager.get(menu);
            me.menu.ownerItem = me;
        } else {
            me.menu = null;
        }
        
        if (me.rendered && !me.destroying && arrowEl) {
            arrowEl[me.menu ? 'addCls' : 'removeCls'](me.arrowCls);
        }
    },

    
    setHandler: function(fn, scope) {
        this.handler = fn || null;
        this.scope = scope;
    },

    
    setIcon: function(icon){
        var iconEl = this.iconEl,
            oldIcon = this.icon;
        if (iconEl) {
            iconEl.src = icon || Ext.BLANK_IMAGE_URL;
        }
        this.icon = icon;
        this.fireEvent('iconchange', this, oldIcon, icon);
    },

    
    setIconCls: function(iconCls) {
        var me = this,
            iconEl = me.iconEl,
            oldCls = me.iconCls;

        if (iconEl) {
            if (me.iconCls) {
                iconEl.removeCls(me.iconCls);
            }

            if (iconCls) {
                iconEl.addCls(iconCls);
            }
        }

        me.iconCls = iconCls;
        me.fireEvent('iconchange', me, oldCls, iconCls);
    },

    
    setText: function(text) {
        var me = this,
            el = me.textEl || me.el,
            oldText = me.text;

        me.text = text;

        if (me.rendered) {
            el.update(text || '');
            
            me.ownerCt.updateLayout();
        }
        me.fireEvent('textchange', me, oldText, text);
    },

    getTipAttr: function(){
        return this.tooltipType == 'qtip' ? 'data-qtip' : 'title';
    },

    
    clearTip: function() {
        if (Ext.quickTipsActive && Ext.isObject(this.tooltip)) {
            Ext.tip.QuickTipManager.unregister(this.itemEl);
        }
    },

    
    setTooltip: function(tooltip, initial) {
        var me = this;

        if (me.rendered) {
            if (!initial) {
                me.clearTip();
            }

            if (Ext.quickTipsActive && Ext.isObject(tooltip)) {
                Ext.tip.QuickTipManager.register(Ext.apply({
                    target: me.itemEl.id
                },
                tooltip));
                me.tooltip = tooltip;
            } else {
                me.itemEl.dom.setAttribute(me.getTipAttr(), tooltip);
            }
        } else {
            me.tooltip = tooltip;
        }

        return me;
    }

});


Ext.define('Ext.menu.CheckItem', {
    extend:  Ext.menu.Item ,
    alias: 'widget.menucheckitem',
    
    
    
    

    
    
    

    
    checkedCls: Ext.baseCSSPrefix + 'menu-item-checked',
    
    uncheckedCls: Ext.baseCSSPrefix + 'menu-item-unchecked',
    
    groupCls: Ext.baseCSSPrefix + 'menu-group-icon',

    
    hideOnClick: false,
    
    
    checkChangeDisabled: false,
    
    ariaRole: 'menuitemcheckbox',

    childEls: [
        'itemEl', 'iconEl', 'textEl', 'checkEl'
    ],
    
    showCheckbox: true,

    isMenuCheckItem: true,

    checkboxCls: Ext.baseCSSPrefix + 'menu-item-checkbox',

    initComponent: function() {
        var me = this;
        
        
        me.checked = !!me.checked;
        me.addEvents(
            
            'beforecheckchange',

            
            'checkchange'
        );

        me.callParent(arguments);

        Ext.menu.Manager.registerCheckable(me);

        if (me.group) {
            if (me.initialConfig.hideOnClick !== false) {
                me.hideOnClick = true;
            }
        }
    },
    
    beforeRender: function() {
        var me = this;

        me.callParent();
        Ext.apply(me.renderData, {
            checkboxCls: me.checkboxCls,
            showCheckbox: me.showCheckbox
        });
    },
    
    afterRender: function() {
        var me = this;
        me.callParent();
        me.checked = !me.checked;
        me.setChecked(!me.checked, true);
        if (me.checkChangeDisabled) {
            me.disableCheckChange();
        }
    },
    
    
    disableCheckChange: function() {
        var me = this,
            checkEl = me.checkEl;

        if (checkEl) {
            checkEl.addCls(me.disabledCls);
        }
        
        
        if (!(Ext.isIE10p || (Ext.isIE9 && Ext.isStrict)) && me.rendered) {
            me.el.repaint();
        }
        me.checkChangeDisabled = true;
    },

    
    enableCheckChange: function() {
        var me = this,
            checkEl = me.checkEl;
            
        if (checkEl) {
            checkEl.removeCls(me.disabledCls);
        }
        me.checkChangeDisabled = false;
    },

    onClick: function(e) {
        var me = this;
        if(!me.disabled && !me.checkChangeDisabled && !(me.checked && me.group)) {
            me.setChecked(!me.checked);
        }
        this.callParent([e]);
    },

    onDestroy: function() {
        Ext.menu.Manager.unregisterCheckable(this);
        this.callParent(arguments);
    },

    
    setChecked: function(checked, suppressEvents) {
        var me = this,
            checkedCls = me.checkedCls,
            uncheckedCls = me.uncheckedCls,
            el = me.el;
            
        if (me.checked !== checked && (suppressEvents || me.fireEvent('beforecheckchange', me, checked) !== false)) {
            if (el) {
                if (checked) {
                    el.addCls(checkedCls);
                    el.removeCls(uncheckedCls);
                } else {
                    el.addCls(uncheckedCls);
                    el.removeCls(checkedCls);
                }
            }
            me.checked = checked;
            Ext.menu.Manager.onCheckChange(me, checked);
            if (!suppressEvents) {
                Ext.callback(me.checkHandler, me.scope || me, [me, checked]);
                me.fireEvent('checkchange', me, checked);
            }
        }
    }
});


Ext.define('Ext.menu.KeyNav', {
    extend:  Ext.util.KeyNav ,

                                   
    
    constructor: function(config) {
        var me = this;

        me.menu = config.target;
        me.callParent([Ext.apply({
            down: me.down,
            enter: me.enter,
            esc: me.escape,
            left: me.left,
            right: me.right,
            space: me.enter,
            tab: me.tab,
            up: me.up
        }, config)]);
    },

    down: function(e) {
        var me = this,
            fi = me.menu.focusedItem;

        if (fi && e.getKey() == Ext.EventObject.DOWN && me.isWhitelisted(fi)) {
            return true;
        }
        me.focusNextItem(1);
    },

    enter: function(e) {
        var menu = this.menu,
            focused = menu.focusedItem;
 
        if (menu.activeItem) {
            menu.onClick(e);
        } else if (focused && focused.isFormField) {
            
            return true;
        }
    },

    escape: function(e) {
        Ext.menu.Manager.hideAll();
    },

    focusNextItem: function(step) {
        var menu = this.menu,
            items = menu.items,
            focusedItem = menu.focusedItem,
            startIdx = focusedItem ? items.indexOf(focusedItem) : -1,
            idx = startIdx + step,
            len = items.length,
            count = 0,
            item;

        
        while (count < len && idx !== startIdx) {
            if (idx < 0) {
                idx = len - 1;
            } else if (idx >= len) {
                idx = 0;
            }

            item = items.getAt(idx);
            if (menu.canActivateItem(item)) {
                menu.setActiveItem(item);
                break;
            }
            idx += step;
            ++count;
        }
    },

    isWhitelisted: function(item) {
        return Ext.FocusManager.isWhitelisted(item);
    },

    left: function(e) {
        var menu = this.menu,
            fi = menu.focusedItem;

        if (fi && this.isWhitelisted(fi)) {
            return true;
        }

        menu.hide();
        if (menu.parentMenu) {
            menu.parentMenu.focus();
        }
    },

    right: function(e) {
        var menu = this.menu,
            fi = menu.focusedItem,
            ai = menu.activeItem,
            am;

        if (fi && this.isWhitelisted(fi)) {
            return true;
        }

        if (ai) {
            am = menu.activeItem.menu;
            if (am) {
                ai.expandMenu(0);
                am.setActiveItem(am.child(':focusable'));
            }
        }
    },

    tab: function(e) {
        var me = this;

        if (e.shiftKey) {
            me.up(e);
        } else {
            me.down(e);
        }
    },

    up: function(e) {
        var me = this,
            fi = me.menu.focusedItem;

        if (fi && e.getKey() == Ext.EventObject.UP && me.isWhitelisted(fi)) {
            return true;
        }
        me.focusNextItem(-1);
    }
});


Ext.define('Ext.menu.Separator', {
    extend:  Ext.menu.Item ,
    alias: 'widget.menuseparator',

    

    
    canActivate: false,

    

    

    

    focusable: false,

    

    

    
    hideOnClick: false,

    

    

    

    

    

    

    
    plain: true,

    
    separatorCls: Ext.baseCSSPrefix + 'menu-item-separator',

    
    text: '&#160;',
    
    ariaRole: 'separator',

    beforeRender: function(ct, pos) {
        var me = this;

        me.callParent();

        me.addCls(me.separatorCls);
    }
});


Ext.define('Ext.menu.Menu', {
    extend:  Ext.panel.Panel ,
    alias: 'widget.menu',
               
                                    
                             
                        
                          
                           
                            
      

    
    
    
    enableKeyNav: true,

    
    allowOtherMenus: false,

    
    ariaRole: 'menu',

    

    
    floating: true,

    
    constrain: true,

    
    hidden: true,

    hideMode: 'visibility',

    
    ignoreParentClicks: false,

    
    isMenu: true,

    

    
    showSeparator : true,

    
    minWidth: undefined,

    defaultMinWidth: 120,

    

    initComponent: function() {
        var me = this,
            prefix = Ext.baseCSSPrefix,
            cls = [prefix + 'menu'],
            bodyCls = me.bodyCls ? [me.bodyCls] : [],
            isFloating = me.floating !== false;

        me.addEvents(
            
            'click',

            
            'mouseenter',

            
            'mouseleave',

            
            'mouseover'
        );

        Ext.menu.Manager.register(me);

        
        if (me.plain) {
            cls.push(prefix + 'menu-plain');
        }
        me.cls = cls.join(' ');

        
        bodyCls.push(prefix + 'menu-body', Ext.dom.Element.unselectableCls);
        me.bodyCls = bodyCls.join(' ');

        
        
        
        
        if (!me.layout) {
            me.layout = {
                type: 'vbox',
                align: 'stretchmax',
                overflowHandler: 'Scroller'
            };
        }

        if (isFloating)  {
            
            if (me.minWidth === undefined) {
                me.minWidth = me.defaultMinWidth;
            }
        } else {
            
            me.hidden = !!me.initialConfig.hidden;
            me.constrain = false;
        }

        me.callParent(arguments);
    },

    
    
    registerWithOwnerCt: function() {
        if (this.floating) {
            this.ownerCt = null;
            Ext.WindowManager.register(this);
        }
    },

    
    
    initHierarchyEvents: Ext.emptyFn,

    
    isVisible: function() {
        return this.callParent();
    },

    
    
    getHierarchyState: function() {
        var result = this.callParent();
        result.hidden = this.hidden;
        return result;
    },

    beforeRender: function() {
        this.callParent(arguments);

        
        
        if (!this.getSizeModel().width.shrinkWrap) {
            this.layout.align = 'stretch';
        }
    },

    onBoxReady: function() {
        var me = this;

        me.callParent(arguments);

        
        if (me.showSeparator) {
            me.iconSepEl = me.layout.getElementTarget().insertFirst({
                role: 'presentation',
                cls: Ext.baseCSSPrefix + 'menu-icon-separator',
                html: '&#160;'
            });
        }

        me.mon(me.el, {
            click: me.onClick,
            mouseover: me.onMouseOver,
            scope: me
        });
        me.mouseMonitor = me.el.monitorMouseLeave(100, me.onMouseLeave, me);

        
        if (me.enableKeyNav) {
            me.keyNav = new Ext.menu.KeyNav({
                target: me,
                keyMap: me.getKeyMap()
            });
        }
    },

    getRefOwner: function() {
        
        
        
        return this.parentMenu || this.ownerButton || this.callParent(arguments);
    },

    
    canActivateItem: function(item) {
        return item && !item.isDisabled() && item.isVisible() && (item.canActivate || !item.isMenuItem);
    },

    
    deactivateActiveItem: function(andBlurFocusedItem) {
        var me = this,
            activeItem = me.activeItem,
            focusedItem = me.focusedItem;

        if (activeItem) {
            activeItem.deactivate();
            if (!activeItem.activated) {
                delete me.activeItem;
            }
        }

        
        
        if (focusedItem && andBlurFocusedItem) {
            focusedItem.blur();
            delete me.focusedItem;
        }
    },

    
    getFocusEl: function() {
        return this.focusedItem || this.el;
    },

    
    hide: function() {
        this.deactivateActiveItem(true);
        this.callParent(arguments);
    },

    
    getItemFromEvent: function(e) {
        var me = this,
            renderTarget = me.layout.getRenderTarget().dom,
            toEl = e.getTarget();

        
        while (toEl.parentNode !== renderTarget) {
            toEl = toEl.parentNode;
            if (!toEl) {
                return;
            }
        }
        return Ext.getCmp(toEl.id);
    },

    lookupComponent: function(cmp) {
        var me = this;

        if (typeof cmp == 'string') {
            cmp = me.lookupItemFromString(cmp);
        } else if (Ext.isObject(cmp)) {
            cmp = me.lookupItemFromObject(cmp);
        }

        
        
        cmp.minWidth = cmp.minWidth || me.minWidth;

        return cmp;
    },

    
    lookupItemFromObject: function(cmp) {
        var me = this,
            prefix = Ext.baseCSSPrefix,
            cls;

        if (!cmp.isComponent) {
            if (!cmp.xtype) {
                cmp = Ext.create('Ext.menu.' + (Ext.isBoolean(cmp.checked) ? 'Check': '') + 'Item', cmp);
            } else {
                cmp = Ext.ComponentManager.create(cmp, cmp.xtype);
            }
        }

        if (cmp.isMenuItem) {
            cmp.parentMenu = me;
        }

        if (!cmp.isMenuItem && !cmp.dock) {
            cls = [prefix + 'menu-item-cmp'];

            
            
            
            if (!me.plain && (cmp.indent !== false || cmp.iconCls === 'no-icon')) {
                cls.push(prefix + 'menu-item-indent');
            }

            if (cmp.rendered) {
                cmp.el.addCls(cls);
            } else {
                cmp.cls = (cmp.cls || '') + ' ' + cls.join(' ');
            }
        }
        return cmp;
    },

    
    lookupItemFromString: function(cmp) {
        return (cmp == 'separator' || cmp == '-') ?
            new Ext.menu.Separator()
            : new Ext.menu.Item({
                canActivate: false,
                hideOnClick: false,
                plain: true,
                text: cmp
            });
    },

    onClick: function(e) {
        var me = this,
            item;

        if (me.disabled) {
            e.stopEvent();
            return;
        }

        item = (e.type === 'click') ? me.getItemFromEvent(e) : me.activeItem;
        if (item && item.isMenuItem) {
            if (!item.menu || !me.ignoreParentClicks) {
                item.onClick(e);
            } else {
                e.stopEvent();
            }
        }
        
        if (!item || item.disabled) {
            item = undefined;
        }
        me.fireEvent('click', me, item, e);
    },

    onDestroy: function() {
        var me = this;

        Ext.menu.Manager.unregister(me);
        me.parentMenu = me.ownerButton = null;
        if (me.rendered) {
            me.el.un(me.mouseMonitor);
            Ext.destroy(me.keyNav);
            me.keyNav = null;
        }
        me.callParent(arguments);
    },

    onMouseLeave: function(e) {
        var me = this;

        me.deactivateActiveItem();

        if (me.disabled) {
            return;
        }

        me.fireEvent('mouseleave', me, e);
    },

    onMouseOver: function(e) {
        var me = this,
            fromEl = e.getRelatedTarget(),
            mouseEnter = !me.el.contains(fromEl),
            item = me.getItemFromEvent(e),
            parentMenu = me.parentMenu,
            parentItem = me.parentItem;

        if (mouseEnter && parentMenu) {
            parentMenu.setActiveItem(parentItem);
            parentItem.cancelDeferHide();
            parentMenu.mouseMonitor.mouseenter();
        }

        if (me.disabled) {
            return;
        }

        
        if (item && !item.activated) {
            me.setActiveItem(item);
            if (item.activated && item.expandMenu) {
                item.expandMenu();
            }
        }
        if (mouseEnter) {
            me.fireEvent('mouseenter', me, e);
        }
        me.fireEvent('mouseover', me, item, e);
    },

    setActiveItem: function(item) {
        var me = this;

        if (item && (item != me.activeItem)) {
            me.deactivateActiveItem();
            if (me.canActivateItem(item)) {
                if (item.activate) {
                    
                    item.activate(true);
                    if (item.activated) {
                        me.activeItem = item;
                        me.focusedItem = item;
                    }
                } else {
                    item.focus();
                    me.focusedItem = item;
                }
            }
            
        }
    },

    showBy: function(cmp, pos, off) {
        var me = this;

        me.callParent(arguments);
        if (!me.hidden) {
            
            me.setVerticalPosition();
        }
        return me;
    },

    beforeShow: function() {
        var me = this,
            viewHeight;

        
        if (me.floating) {
            me.savedMaxHeight = me.maxHeight;
            viewHeight = me.container.getViewSize().height;
            me.maxHeight = Math.min(me.maxHeight || viewHeight, viewHeight);
        }

        me.callParent(arguments);
    },

    afterShow: function() {
        var me = this;

        me.callParent(arguments);

        
        if (me.floating) {
            me.maxHeight = me.savedMaxHeight;
        }
    },

    
    
    
    setVerticalPosition: function() {
        var me = this,
            max,
            y = me.getY(),
            returnY = y,
            height = me.getHeight(),
            viewportHeight = Ext.Element.getViewportHeight().height,
            parentEl = me.el.parent(),
            viewHeight = parentEl.getViewSize().height,
            normalY = y - parentEl.getScroll().top; 

        parentEl = null;

        if (me.floating) {
            max = me.maxHeight ? me.maxHeight : viewHeight - normalY;
            if (height > viewHeight) {
                returnY = y - normalY;
            } else if (max < height) {
                returnY = y - (height - max);
            } else if((y + height) > viewportHeight){ 
                returnY = viewportHeight - height;
            }
        }
        me.setY(returnY);
    }
});


 Ext.define('Ext.menu.ColorPicker', {
     extend:  Ext.menu.Menu ,

     alias: 'widget.colormenu',

                
                          
       

    
    hideOnClick : true,

    
    pickerId : null,

    

    

    

    initComponent : function(){
        var me = this,
            cfg = Ext.apply({}, me.initialConfig);

        
        delete cfg.listeners;
        Ext.apply(me, {
            plain: true,
            showSeparator: false,
            bodyPadding: 0,
            items: Ext.applyIf({
                cls: Ext.baseCSSPrefix + 'menu-color-item',
                margin: 0,
                id: me.pickerId,
                xtype: 'colorpicker'
            }, cfg)
        });

        me.callParent(arguments);

        me.picker = me.down('colorpicker');

        
        me.relayEvents(me.picker, ['select']);

        if (me.hideOnClick) {
            me.on('select', me.hidePickerOnSelect, me);
        }
    },

    
    hidePickerOnSelect: function() {
        Ext.menu.Manager.hideAll();
    }
 });


 Ext.define('Ext.menu.DatePicker', {
     extend:  Ext.menu.Menu ,

     alias: 'widget.datemenu',

                
                         
       

    
    hideOnClick : true,

    
    pickerId : null,

    

    

    initComponent : function(){
        var me = this,
            cfg = Ext.apply({}, me.initialConfig);
            
        
        delete cfg.listeners;
            
        Ext.apply(me, {
            showSeparator: false,
            plain: true,
            bodyPadding: 0, 
            items: Ext.applyIf({
                cls: Ext.baseCSSPrefix + 'menu-date-item',
                margin: 0,
                border: false,
                id: me.pickerId,
                xtype: 'datepicker'
            }, cfg)
        });

        me.callParent(arguments);

        me.picker = me.down('datepicker');
        
        me.relayEvents(me.picker, ['select']);

        if (me.hideOnClick) {
            me.on('select', me.hidePickerOnSelect, me);
        }
    },

    hidePickerOnSelect: function() {
        Ext.menu.Manager.hideAll();
    }
 });


Ext.define('Ext.panel.Tool', {
    extend:  Ext.Component ,
                                          
    alias: 'widget.tool',

    
    isTool: true,

    baseCls: Ext.baseCSSPrefix + 'tool',
    disabledCls: Ext.baseCSSPrefix + 'tool-disabled',
    
    
    toolPressedCls: Ext.baseCSSPrefix + 'tool-pressed',
    
    toolOverCls: Ext.baseCSSPrefix + 'tool-over',

    ariaRole: 'button',

    childEls: [
        'toolEl'
    ],

    renderTpl: [
        '<img id="{id}-toolEl" src="{blank}" class="{baseCls}-img {baseCls}-{type}' +
            '{childElCls}" role="presentation"/>'
    ],

    
    toolOwner: null,

    

    

    

    

    

     
    tooltipType: 'qtip',

    
    stopEvent: true,

    
    height: 15,
    width: 15,


    initComponent: function() {
        var me = this;
        me.addEvents(
            
            'click'
        );


        me.type = me.type || me.id;

        Ext.applyIf(me.renderData, {
            baseCls: me.baseCls,
            blank: Ext.BLANK_IMAGE_URL,
            type: me.type
        });

        
        me.tooltip = me.tooltip || me.qtip;
        me.callParent();
    },

    
    afterRender: function() {
        var me = this,
            attr;

        me.callParent(arguments);

        me.el.on({
            click: me.onClick,
            mousedown: me.onMouseDown,
            mouseover: me.onMouseOver,
            mouseout: me.onMouseOut,
            scope: me
        });

        if (me.tooltip) {
            if (Ext.quickTipsActive && Ext.isObject(me.tooltip)) {
                Ext.tip.QuickTipManager.register(Ext.apply({
                    target: me.id
                }, me.tooltip));
            }
            else {
                attr = me.tooltipType == 'qtip' ? 'data-qtip' : 'title';
                me.el.dom.setAttribute(attr, me.tooltip);
            }
        }
    },

    getFocusEl: function() {
        return this.el;
    },

    
    setType: function(type) {
        var me = this,
            oldType = me.type;

        me.type = type;
        if (me.rendered) {
            if (oldType) {
                me.toolEl.removeCls(me.baseCls + '-' + oldType);
            }
            me.toolEl.addCls(me.baseCls + '-' + type);
        } else {
            me.renderData.type = type;
        }
        return me;
    },

    
    onClick: function(e, target) {
        var me = this;

        if (me.disabled) {
            return false;
        }

        
        me.el.removeCls(me.toolPressedCls);
        me.el.removeCls(me.toolOverCls);

        if (me.stopEvent !== false) {
            e.stopEvent();
        }

        if (me.handler) {
            Ext.callback(me.handler, me.scope || me, [e, target, me.ownerCt, me]);
        } else if (me.callback) {
            Ext.callback(me.callback, me.scope || me, [me.toolOwner || me.ownerCt, me, e]);
        }
        me.fireEvent('click', me, e);
        return true;
    },

    
    onDestroy: function(){
        var me = this;
        
        if (Ext.quickTipsActive && Ext.isObject(me.tooltip)) {
            Ext.tip.QuickTipManager.unregister(me.id);
        }
        
        
        if (me.keyMap) {
            me.keyMap.destroy();
        }
        
        me.callParent();
    },

    
    onMouseDown: function() {
        if (this.disabled) {
            return false;
        }

        this.el.addCls(this.toolPressedCls);
    },

    
    onMouseOver: function() {
        if (this.disabled) {
            return false;
        }
        this.el.addCls(this.toolOverCls);
    },

    
    onMouseOut: function() {
        this.el.removeCls(this.toolOverCls);
    }
});


Ext.define('Ext.resizer.SplitterTracker', {
    extend:  Ext.dd.DragTracker ,
                                  
    enabled: true,
    
    overlayCls: Ext.baseCSSPrefix + 'resizable-overlay',

    createDragOverlay: function () {
        var overlay;

        overlay = this.overlay =  Ext.getBody().createChild({
            role: 'presentation',
            cls: this.overlayCls, 
            html: '&#160;'
        });

        overlay.unselectable();
        overlay.setSize(Ext.Element.getViewWidth(true), Ext.Element.getViewHeight(true));
        overlay.show();
    },

    getPrevCmp: function() {
        var splitter = this.getSplitter();
        return splitter.previousSibling(':not([hidden])');
    },

    getNextCmp: function() {
        var splitter = this.getSplitter();
        return splitter.nextSibling(':not([hidden])');
    },

    
    
    onBeforeStart: function(e) {
        var me = this,
            prevCmp = me.getPrevCmp(),
            nextCmp = me.getNextCmp(),
            collapseEl = me.getSplitter().collapseEl,
            target = e.getTarget(),
            box;
            
        if (!prevCmp || !nextCmp) {
            return false;
        }

        if (collapseEl && target === me.getSplitter().collapseEl.dom) {
            return false;
        }

        
        if (nextCmp.collapsed || prevCmp.collapsed) {
            return false;
        }

        
        me.prevBox  = prevCmp.getEl().getBox();
        me.nextBox  = nextCmp.getEl().getBox();
        me.constrainTo = box = me.calculateConstrainRegion();

        if (!box) {
            return false;
        }

        return box;
    },

    
    onStart: function(e) {
        var splitter = this.getSplitter();
        this.createDragOverlay();
        splitter.addCls(splitter.baseCls + '-active');
    },

    
    calculateConstrainRegion: function() {
        var me         = this,
            splitter   = me.getSplitter(),
            splitWidth = splitter.getWidth(),
            defaultMin = splitter.defaultSplitMin,
            orient     = splitter.orientation,
            prevBox    = me.prevBox,
            prevCmp    = me.getPrevCmp(),
            nextBox    = me.nextBox,
            nextCmp    = me.getNextCmp(),
            
            
            
            prevConstrainRegion, nextConstrainRegion, constrainOptions;

        
        if (orient === 'vertical') {
            constrainOptions = {
                prevCmp: prevCmp,
                nextCmp: nextCmp,
                prevBox: prevBox,
                nextBox: nextBox,
                defaultMin: defaultMin,
                splitWidth: splitWidth
            };
            
            
            prevConstrainRegion = new Ext.util.Region(
                prevBox.y,
                me.getVertPrevConstrainRight(constrainOptions),
                prevBox.bottom,
                me.getVertPrevConstrainLeft(constrainOptions)
            );
            
            nextConstrainRegion = new Ext.util.Region(
                nextBox.y,
                me.getVertNextConstrainRight(constrainOptions),
                nextBox.bottom,
                me.getVertNextConstrainLeft(constrainOptions)
            );
        } else {
            
            prevConstrainRegion = new Ext.util.Region(
                prevBox.y + (prevCmp.minHeight || defaultMin),
                prevBox.right,
                
                
                (prevCmp.maxHeight ? prevBox.y + prevCmp.maxHeight : nextBox.bottom - (nextCmp.minHeight || defaultMin)) + splitWidth,
                prevBox.x
            );
            
            nextConstrainRegion = new Ext.util.Region(
                
                
                (nextCmp.maxHeight ? nextBox.bottom - nextCmp.maxHeight : prevBox.y + (prevCmp.minHeight || defaultMin)) - splitWidth,
                nextBox.right,
                nextBox.bottom - (nextCmp.minHeight || defaultMin),
                nextBox.x
            );
        }

        
        return prevConstrainRegion.intersect(nextConstrainRegion);
    },

    
    performResize: function(e, offset) {
        var me        = this,
            splitter  = me.getSplitter(),
            orient    = splitter.orientation,
            prevCmp   = me.getPrevCmp(),
            nextCmp   = me.getNextCmp(),
            owner     = splitter.ownerCt,
            flexedSiblings = owner.query('>[flex]'),
            len       = flexedSiblings.length,
            vertical  = orient === 'vertical',
            i         = 0,
            dimension = vertical ? 'width' : 'height',
            totalFlex = 0,
            item, size;

        
        for (; i < len; i++) {
            item = flexedSiblings[i];
            size = vertical ? item.getWidth() : item.getHeight();
            totalFlex += size;
            item.flex = size;
        }

        offset = vertical ? offset[0] : offset[1];

        if (prevCmp) {
            size = me.prevBox[dimension] + offset;
            if (prevCmp.flex) {
                prevCmp.flex = size;
            } else {
                prevCmp[dimension] = size;
            }
        }
        if (nextCmp) {
            size = me.nextBox[dimension] - offset;
            if (nextCmp.flex) {
                nextCmp.flex = size;
            } else {
                nextCmp[dimension] = size;
            }
        }

        owner.updateLayout();
    },

    
    
    
    endDrag: function () {
        var me = this;

        if (me.overlay) {
             me.overlay.remove();
             delete me.overlay;
        }

        me.callParent(arguments); 
    },

    
    onEnd: function(e) {
        var me = this,
            splitter = me.getSplitter();
            
        splitter.removeCls(splitter.baseCls + '-active');
        me.performResize(e, me.getResizeOffset());
    },

    
    
    onDrag: function(e) {
        var me        = this,
            offset    = me.getOffset('dragTarget'),
            splitter  = me.getSplitter(),
            splitEl   = splitter.getEl(),
            orient    = splitter.orientation;

        if (orient === "vertical") {
            splitEl.setX(me.startRegion.left + offset[0]);
        } else {
            splitEl.setY(me.startRegion.top + offset[1]);
        }
    },

    getSplitter: function() {
        return this.splitter;
    },

    getVertPrevConstrainRight: function(o) {
        
        
        return (o.prevCmp.maxWidth ? o.prevBox.x + o.prevCmp.maxWidth :
            o.nextBox.right - (o.nextCmp.minWidth || o.defaultMin)) + o.splitWidth;
    },

    getVertPrevConstrainLeft: function(o) {
        return o.prevBox.x + (o.prevCmp.minWidth || o.defaultMin);
    },


    getVertNextConstrainRight: function(o) {
        return o.nextBox.right - (o.nextCmp.minWidth || o.defaultMin);
    },

    getVertNextConstrainLeft: function(o) {
        
        
        return (o.nextCmp.maxWidth ? o.nextBox.right - o.nextCmp.maxWidth :
            o.prevBox.x + (o.prevBox.minWidth || o.defaultMin)) - o.splitWidth;
    },

    getResizeOffset: function() {
        return this.getOffset('dragTarget');
    }
});


Ext.define('Ext.resizer.BorderSplitterTracker', {
    extend:  Ext.resizer.SplitterTracker ,
                                  

    getPrevCmp: null,
    getNextCmp: null,

    
    calculateConstrainRegion: function() {
        var me = this,
            splitter = me.splitter,
            collapseTarget = splitter.collapseTarget,
            defaultSplitMin = splitter.defaultSplitMin,
            sizePropCap = splitter.vertical ? 'Width' : 'Height',
            minSizeProp = 'min' + sizePropCap,
            maxSizeProp = 'max' + sizePropCap,
            getSizeMethod = 'get' + sizePropCap,
            neighbors = splitter.neighbors,
            length = neighbors.length,
            box = collapseTarget.el.getBox(),
            left = box.x,
            top = box.y,
            right = box.right,
            bottom = box.bottom,
            size = splitter.vertical ? (right - left) : (bottom - top),
            
            i, neighbor, minRange, maxRange, maxGrowth, maxShrink, targetSize;

        
        minRange = (collapseTarget[minSizeProp] || Math.min(size,defaultSplitMin)) - size;

        
        maxRange = collapseTarget[maxSizeProp];
        if (!maxRange) {
            maxRange = 1e9;
        } else {
            maxRange -= size;
        }
        targetSize = size;

        for (i = 0; i < length; ++i) {
            neighbor = neighbors[i];
            size = neighbor[getSizeMethod]();
            

            maxGrowth = size - neighbor[maxSizeProp]; 
            maxShrink = size - (neighbor[minSizeProp] || Math.min(size,defaultSplitMin));

            if (!isNaN(maxGrowth)) {
                
                
                if (minRange < maxGrowth) {
                    minRange = maxGrowth;
                }
            }

            
            
            if (maxRange > maxShrink) {
                maxRange = maxShrink;
            }
        }

        if (maxRange - minRange < 2) {
            return null;
        }

        box = new Ext.util.Region(top, right, bottom, left);

        me.constraintAdjusters[me.getCollapseDirection()](box, minRange, maxRange, splitter);

        me.dragInfo = {
            minRange: minRange,
            maxRange: maxRange,
            
            targetSize: targetSize
        };

        return box;
    },

    constraintAdjusters: {
        
        left: function (box, minRange, maxRange, splitter) {
            box[0] = box.x = box.left = box.right + minRange;
            box.right += maxRange + splitter.getWidth();
        },

        
        top: function (box, minRange, maxRange, splitter) {
            box[1] = box.y = box.top = box.bottom + minRange;
            box.bottom += maxRange + splitter.getHeight();
        },

        
        bottom: function (box, minRange, maxRange, splitter) {
            box.bottom = box.top - minRange;
            box.top -= maxRange + splitter.getHeight();
        },

        
        right: function (box, minRange, maxRange, splitter) {
            box.right = box.left - minRange;
            box[0] = box.x = box.left = box.x - maxRange + splitter.getWidth();
        }
    },

    onBeforeStart: function(e) {
        var me = this,
            splitter = me.splitter,
            collapseTarget = splitter.collapseTarget,
            neighbors = splitter.neighbors,
            collapseEl = me.getSplitter().collapseEl,
            target = e.getTarget(),
            length = neighbors.length,
            i, neighbor;

        if (collapseEl && target === splitter.collapseEl.dom) {
            return false;
        }

        if (collapseTarget.collapsed) {
            return false;
        }

        
        for (i = 0; i < length; ++i) {
            neighbor = neighbors[i];

            if (neighbor.collapsed && neighbor.isHorz === collapseTarget.isHorz) {
                return false;
            }
        }

        if (!(me.constrainTo = me.calculateConstrainRegion())) {
            return false;
        }

        return true;
    },

    performResize: function(e, offset) {
        var me = this,
            splitter = me.splitter,
            collapseDirection = splitter.getCollapseDirection(),
            collapseTarget = splitter.collapseTarget,
            
            adjusters = me.splitAdjusters[splitter.vertical ? 'horz' : 'vert'],
            delta = offset[adjusters.index],
            dragInfo = me.dragInfo,
            
            
            
            
            
            owner;

        if (collapseDirection == 'right' || collapseDirection == 'bottom') {
            
            delta = -delta;
        }

        
        delta = Math.min(Math.max(dragInfo.minRange, delta), dragInfo.maxRange);

        if (delta) {
            (owner = splitter.ownerCt).suspendLayouts();

            adjusters.adjustTarget(collapseTarget, dragInfo.targetSize, delta);

            
            
            
            
            
            
            

            owner.resumeLayouts(true);
        }
    },

    splitAdjusters: {
        horz: {
            index: 0,
            
            
            
            adjustTarget: function (target, size, delta) {
                target.flex = null;
                target.setSize(size + delta);
            }
        },
        vert: {
            index: 1,
            
            
            
            adjustTarget: function (target, targetSize, delta) {
                target.flex = null;
                target.setSize(undefined, targetSize + delta);
            }
        }
    },

    getCollapseDirection: function() {
        return this.splitter.getCollapseDirection();
    }
});


Ext.define('Ext.resizer.Handle', {
    extend:  Ext.Component ,
    handleCls: '',
    baseHandleCls: Ext.baseCSSPrefix + 'resizable-handle',
    
    
    region: '',
    
    ariaRole: 'presentation',

    beforeRender: function() {
        var me = this;

        me.callParent();

        me.protoEl.unselectable();

        me.addCls(
            me.baseHandleCls,
            me.baseHandleCls + '-' + me.region,
            me.handleCls
        );
    }
});


Ext.define('Ext.resizer.ResizeTracker', {
    extend:  Ext.dd.DragTracker ,
    dynamic: true,
    preserveRatio: false,

    
    constrainTo: null,
    
    proxyCls:  Ext.baseCSSPrefix + 'resizable-proxy',

    constructor: function(config) {
        var me = this,
            widthRatio, heightRatio,
            throttledResizeFn;

        if (!config.el) {
            if (config.target.isComponent) {
                me.el = config.target.getEl();
            } else {
                me.el = config.target;
            }
        }
        this.callParent(arguments);

        
        if (me.preserveRatio && me.minWidth && me.minHeight) {
            widthRatio = me.minWidth / me.el.getWidth();
            heightRatio = me.minHeight / me.el.getHeight();

            
            
            
            if (heightRatio > widthRatio) {
                me.minWidth = me.el.getWidth() * heightRatio;
            } else {
                me.minHeight = me.el.getHeight() * widthRatio;
            }
        }

        
        
        if (me.throttle) {
            throttledResizeFn = Ext.Function.createThrottled(function() {
                    Ext.resizer.ResizeTracker.prototype.resize.apply(me, arguments);
                }, me.throttle);

            me.resize = function(box, direction, atEnd) {
                if (atEnd) {
                    Ext.resizer.ResizeTracker.prototype.resize.apply(me, arguments);
                } else {
                    throttledResizeFn.apply(null, arguments);
                }
            };
        }
    },

    onBeforeStart: function(e) {
        
        this.startBox = this.target.getBox();
    },

    
    getDynamicTarget: function() {
        var me = this,
            target = me.target;
            
        if (me.dynamic) {
            return target;
        } else if (!me.proxy) {
            me.proxy = me.createProxy(target);
        }
        me.proxy.show();
        return me.proxy;
    },

    
    createProxy: function(target){
        var proxy,
            cls = this.proxyCls;

        if (target.isComponent) {
            proxy = target.getProxy().addCls(cls);
        } else {
            proxy = target.createProxy({
                tag: 'div',
                role: 'presentation',
                cls: cls,
                id: target.id + '-rzproxy'
            }, Ext.getBody());
        }
        proxy.removeCls(Ext.baseCSSPrefix + 'proxy-el');
        return proxy;
    },

    onStart: function(e) {
        
        this.activeResizeHandle = Ext.get(this.getDragTarget().id);

        
        if (!this.dynamic) {
            this.resize(this.startBox, {
                horizontal: 'none',
                vertical: 'none'
            });
        }
    },

    onDrag: function(e) {
        
        if (this.dynamic || this.proxy) {
            this.updateDimensions(e);
        }
    },

    updateDimensions: function(e, atEnd) {
        var me = this,
            region = me.activeResizeHandle.region,
            offset = me.getOffset(me.constrainTo ? 'dragTarget' : null),
            box = me.startBox,
            ratio,
            widthAdjust = 0,
            heightAdjust = 0,
            snappedWidth,
            snappedHeight,
            adjustX = 0,
            adjustY = 0,
            dragRatio,
            horizDir = offset[0] < 0 ? 'right' : 'left',
            vertDir = offset[1] < 0 ? 'down' : 'up',
            oppositeCorner,
            axis, 
            newBox,
            newHeight, newWidth;

        region = me.convertRegionName(region);

        switch (region) {
            case 'south':
                heightAdjust = offset[1];
                axis = 2;
                break;
            case 'north':
                heightAdjust = -offset[1];
                adjustY = -heightAdjust;
                axis = 2;
                break;
            case 'east':
                widthAdjust = offset[0];
                axis = 1;
                break;
            case 'west':
                widthAdjust = -offset[0];
                adjustX = -widthAdjust;
                axis = 1;
                break;
            case 'northeast':
                heightAdjust = -offset[1];
                adjustY = -heightAdjust;
                widthAdjust = offset[0];
                oppositeCorner = [box.x, box.y + box.height];
                axis = 3;
                break;
            case 'southeast':
                heightAdjust = offset[1];
                widthAdjust = offset[0];
                oppositeCorner = [box.x, box.y];
                axis = 3;
                break;
            case 'southwest':
                widthAdjust = -offset[0];
                adjustX = -widthAdjust;
                heightAdjust = offset[1];
                oppositeCorner = [box.x + box.width, box.y];
                axis = 3;
                break;
            case 'northwest':
                heightAdjust = -offset[1];
                adjustY = -heightAdjust;
                widthAdjust = -offset[0];
                adjustX = -widthAdjust;
                oppositeCorner = [box.x + box.width, box.y + box.height];
                axis = 3;
                break;
        }

        newBox = {
            width: box.width + widthAdjust,
            height: box.height + heightAdjust,
            x: box.x + adjustX,
            y: box.y + adjustY
        };

        
        snappedWidth = Ext.Number.snap(newBox.width, me.widthIncrement);
        snappedHeight = Ext.Number.snap(newBox.height, me.heightIncrement);
        if (snappedWidth != newBox.width || snappedHeight != newBox.height){
            switch (region) {
                case 'northeast':
                    newBox.y -= snappedHeight - newBox.height;
                    break;
                case 'north':
                    newBox.y -= snappedHeight - newBox.height;
                    break;
                case 'southwest':
                    newBox.x -= snappedWidth - newBox.width;
                    break;
                case 'west':
                    newBox.x -= snappedWidth - newBox.width;
                    break;
                case 'northwest':
                    newBox.x -= snappedWidth - newBox.width;
                    newBox.y -= snappedHeight - newBox.height;
            }
            newBox.width = snappedWidth;
            newBox.height = snappedHeight;
        }

        
        if (newBox.width < me.minWidth || newBox.width > me.maxWidth) {
            newBox.width = Ext.Number.constrain(newBox.width, me.minWidth, me.maxWidth);

            
            if (adjustX) {
                newBox.x = box.x + (box.width - newBox.width);
            }
        } else {
            me.lastX = newBox.x;
        }
        if (newBox.height < me.minHeight || newBox.height > me.maxHeight) {
            newBox.height = Ext.Number.constrain(newBox.height, me.minHeight, me.maxHeight);

            
            if (adjustY) {
                newBox.y = box.y + (box.height - newBox.height);
            }
        } else {
            me.lastY = newBox.y;
        }

        
        if (me.preserveRatio || e.shiftKey) {
            ratio = me.startBox.width / me.startBox.height;

            
            newHeight = Math.min(Math.max(me.minHeight, newBox.width / ratio), me.maxHeight);
            newWidth = Math.min(Math.max(me.minWidth, newBox.height * ratio), me.maxWidth);

            
            if (axis == 1) {
                newBox.height = newHeight;
            }

            
            else if (axis == 2) {
                newBox.width = newWidth;
            }

            
            else {
                
                
                dragRatio = Math.abs(oppositeCorner[0] - this.lastXY[0]) / Math.abs(oppositeCorner[1] - this.lastXY[1]);

                
                if (dragRatio > ratio) {
                    newBox.height = newHeight;
                } else {
                    newBox.width = newWidth;
                }

                
                if (region == 'northeast') {
                    newBox.y = box.y - (newBox.height - box.height);
                } else if (region == 'northwest') {
                    newBox.y = box.y - (newBox.height - box.height);
                    newBox.x = box.x - (newBox.width - box.width);
                } else if (region == 'southwest') {
                    newBox.x = box.x - (newBox.width - box.width);
                }
            }
        }

        if (heightAdjust === 0) {
            vertDir = 'none';
        }
        if (widthAdjust === 0) {
            horizDir = 'none';
        }
        me.resize(newBox, {
            horizontal: horizDir,
            vertical: vertDir
        }, atEnd);
    },

    getResizeTarget: function(atEnd) {
        return atEnd ? this.target : this.getDynamicTarget();
    },

    resize: function(box, direction, atEnd) {
        var me = this,
            target = me.getResizeTarget(atEnd);

        target.setBox(box);

        
        if (me.originalTarget && (me.dynamic || atEnd)) {
            me.originalTarget.setBox(box);
        }
    },

    onEnd: function(e) {
        this.updateDimensions(e, true);
        if (this.proxy) {
            this.proxy.hide();
        }
    },

    convertRegionName: function(name) {
        return name;
    }
});


Ext.define('Ext.resizer.Resizer', {
    mixins: {
        observable:  Ext.util.Observable 
    },
                                                         

    alternateClassName: 'Ext.Resizable',

    handleCls: Ext.baseCSSPrefix + 'resizable-handle',
    pinnedCls: Ext.baseCSSPrefix + 'resizable-pinned',
    overCls:   Ext.baseCSSPrefix + 'resizable-over',
    wrapCls:   Ext.baseCSSPrefix + 'resizable-wrap',
    delimiterRe: /(?:\s*[,;]\s*)|\s+/,

    
    dynamic: true,

    
    handles: 's e se',

    
    height : null,

    
    width : null,

    
    heightIncrement : 0,

    
    widthIncrement : 0,

    
    minHeight : 20,

    
    minWidth : 20,

    
    maxHeight : 10000,

    
    maxWidth : 10000,

    
    pinned: false,

    
    preserveRatio: false,

    
    transparent: false,

    

    possiblePositions: {
        n:  'north',
        s:  'south',
        e:  'east',
        w:  'west',
        se: 'southeast',
        sw: 'southwest',
        nw: 'northwest',
        ne: 'northeast'
    },

    

    
    
    ariaRole: 'presentation',

    constructor: function(config) {
        var me = this,
            target,
            targetEl,
            tag,
            handles = me.handles,
            handleCls,
            possibles,
            len,
            i = 0,
            pos, 
            handleEls = [],
            eastWestStyle, style,
            box, targetBaseCls,
            unselectableCls = Ext.dom.Element.unselectableCls;

        me.addEvents(
            
            'beforeresize',
            
            'resizedrag',
            
            'resize'
        );

        if (Ext.isString(config) || Ext.isElement(config) || config.dom) {
            target = config;
            config = arguments[1] || {};
            config.target = target;
        }
        
        me.mixins.observable.constructor.call(me, config);

        
        
        target = me.target;
        if (target) {
            if (target.isComponent) {

                
                
                
                target.addClsWithUI('resizable');

                me.el = target.getEl();
                if (target.minWidth) {
                    me.minWidth = target.minWidth;
                }
                if (target.minHeight) {
                    me.minHeight = target.minHeight;
                }
                if (target.maxWidth) {
                    me.maxWidth = target.maxWidth;
                }
                if (target.maxHeight) {
                    me.maxHeight = target.maxHeight;
                }
                if (target.floating) {
                    if (!me.hasOwnProperty('handles')) {
                        me.handles = 'n ne e se s sw w nw';
                    }
                }
            } else {
                me.el = me.target = Ext.get(target);
            }
        }
        
        else {
            me.target = me.el = Ext.get(me.el);
        }

        
        
        
        tag = me.el.dom.tagName.toUpperCase();
        if (tag == 'TEXTAREA' || tag == 'IMG' || tag == 'TABLE') {
            
            me.originalTarget = me.target;
            targetEl = me.el;
            box = targetEl.getBox();
            me.target = me.el = me.el.wrap({
                role: 'presentation',
                cls: me.wrapCls,
                id: me.el.id + '-rzwrap',
                style: targetEl.getStyles('margin-top', 'margin-bottom')
            });

            
            me.el.setPositioning(targetEl.getPositioning());
            targetEl.clearPositioning();
            me.el.setBox(box);

            
            targetEl.setStyle('position', 'absolute');
        }

        
        
        me.el.position();
        if (me.pinned) {
            me.el.addCls(me.pinnedCls);
        }

        
        me.resizeTracker = new Ext.resizer.ResizeTracker({
            disabled: me.disabled,
            target: me.target,
            constrainTo: me.constrainTo,
            overCls: me.overCls,
            throttle: me.throttle,
            originalTarget: me.originalTarget,
            delegate: '.' + me.handleCls,
            dynamic: me.dynamic,
            preserveRatio: me.preserveRatio,
            heightIncrement: me.heightIncrement,
            widthIncrement: me.widthIncrement,
            minHeight: me.minHeight,
            maxHeight: me.maxHeight,
            minWidth: me.minWidth,
            maxWidth: me.maxWidth
        });

        
        me.resizeTracker.on({
            mousedown: me.onBeforeResize,
            drag: me.onResize,
            dragend: me.onResizeEnd,
            scope: me
        });

        if (me.handles == 'all') {
            me.handles = 'n s e w ne nw se sw';
        }

        handles = me.handles = me.handles.split(me.delimiterRe);
        possibles = me.possiblePositions;
        len = handles.length;

        handleCls = me.handleCls + ' ' + me.handleCls + '-{0}';
        if (me.target.isComponent) {
            targetBaseCls = me.target.baseCls
            handleCls += ' ' + targetBaseCls + '-handle ' + targetBaseCls + '-handle-{0}';
            if (Ext.supports.CSS3BorderRadius) {
                handleCls += ' ' + targetBaseCls + '-handle-{0}-br';
            }
        }

        
        eastWestStyle = Ext.isIE6 ? ' style="height:' + me.el.getHeight() + 'px"' : '';

        for (; i < len; i++){
            
            if (handles[i] && possibles[handles[i]]) {
                pos = possibles[handles[i]];
                if (pos === 'east' || pos === 'west') {
                    style = eastWestStyle;
                } else {
                    style = '';
                }

                handleEls.push(
                    '<div id="', me.el.id, '-', pos, '-handle"',
                        ' class="', Ext.String.format(handleCls, pos), ' ', unselectableCls, '"',
                        ' unselectable="on" role="presentation"',
                        style,
                    '></div>'
                );
            }
        }
        Ext.DomHelper.append(me.el, handleEls.join(''));

        
        for (i = 0; i < len; i++){
            
            if (handles[i] && possibles[handles[i]]) {
                pos = possibles[handles[i]];
                me[pos] = me.el.getById(me.el.id + '-' + pos + '-handle');
                me[pos].region = pos;

                if (me.transparent) {
                    me[pos].setOpacity(0);
                }
            }
        }

        
        if (Ext.isNumber(me.width)) {
            me.width = Ext.Number.constrain(me.width, me.minWidth, me.maxWidth);
        }
        if (Ext.isNumber(me.height)) {
            me.height = Ext.Number.constrain(me.height, me.minHeight, me.maxHeight);
        }

        
        if (me.width !== null || me.height !== null) {
            if (me.originalTarget) {
                me.originalTarget.setWidth(me.width);
                me.originalTarget.setHeight(me.height);
            }
            me.resizeTo(me.width, me.height);
        }

        me.forceHandlesHeight();
    },

    disable: function() {
        this.resizeTracker.disable();
    },

    enable: function() {
        this.resizeTracker.enable();
    },

    
    onBeforeResize: function(tracker, e) {
        var box = this.el.getBox();
        return this.fireEvent('beforeresize', this, box.width, box.height, e);
    },

    
    onResize: function(tracker, e) {
        var me = this,
            box = me.el.getBox();
            
        me.forceHandlesHeight();
        return me.fireEvent('resizedrag', me, box.width, box.height, e);
    },

    
    onResizeEnd: function(tracker, e) {
        var me = this,
            box = me.el.getBox();
            
        me.forceHandlesHeight();
        return me.fireEvent('resize', me, box.width, box.height, e);
    },

    
    resizeTo : function(width, height) {
        var me = this;
        me.target.setSize(width, height);
        me.fireEvent('resize', me, width, height, null);
    },

    
    getEl : function() {
        return this.el;
    },

    
    getTarget: function() {
        return this.target;
    },

    destroy: function() {
        var me = this,
            i,
            handles = me.handles,
            len = handles.length,
            positions = me.possiblePositions,
            handle;

        me.resizeTracker.destroy();
        for (i = 0; i < len; i++) {
            if (handle = me[positions[handles[i]]]) {
                handle.remove();
            }
        }
    },

    
    forceHandlesHeight : function() {
        var me = this,
            handle;
        if (Ext.isIE6) {
            handle = me.east;
            if (handle) {
                handle.setHeight(me.el.getHeight());
            }
            handle = me.west;
            if (handle) {
                handle.setHeight(me.el.getHeight());
            }
            me.el.repaint();
        }
    }
});


Ext.define('Ext.selection.CellModel', {
    extend:  Ext.selection.Model ,
    alias: 'selection.cellmodel',
               
                               
                         
      

    


    isCellModel: true,

    
    enableKeyNav: true,

    
    preventWrap: false,

    
    noSelection: {
        row: -1,
        column: -1
    },

    constructor: function() {
        this.addEvents(
            
            'deselect',

            
            'select'
        );
        this.callParent(arguments);
    },

    bindComponent: function(view) {
        var me = this,
            grid = view.ownerCt;
        me.primaryView = view;
        me.views = me.views || [];
        me.views.push(view);
        me.bindStore(view.getStore(), true);

        view.on({
            cellclick: me.onCellClick,
            refresh: me.onViewRefresh,
            scope: me
        });
        if (grid.optimizedColumnMove !== false) {
            grid.on('columnmove', me.onColumnMove, me);
        }

        if (me.enableKeyNav) {
            me.initKeyNav(view);
        }
    },

    initKeyNav: function(view) {
        var me = this;

        if (!view.rendered) {
            view.on('render', Ext.Function.bind(me.initKeyNav, me, [view], 0), me, {single: true});
            return;
        }

        view.el.set({
            tabIndex: -1
        });

        
        
        me.keyNav = new Ext.util.KeyNav({
            target: view.el,
            ignoreInputFields: true,
            up: me.onKeyUp,
            down: me.onKeyDown,
            right: me.onKeyRight,
            left: me.onKeyLeft,
            tab: me.onKeyTab,
            scope: me
        });
    },

    getHeaderCt: function() {
        var selection = this.getCurrentPosition(),
            view = selection ? selection.view : this.primaryView;

        return view.headerCt;
    },

    onKeyUp: function(e) {
        this.doMove('up', e);
    },

    onKeyDown: function(e) {
        this.doMove('down', e);
    },

    onKeyLeft: function(e) {
        this.doMove('left', e);
    },

    onKeyRight: function(e) {
        this.doMove('right', e);
    },

    doMove: function(direction, e){
        this.keyNavigation = true;
        this.move(direction, e);
        this.keyNavigation = false;
    },

    select: function(pos, keepExisting, suppressEvent) {
        var me = this,
            row,
            oldPos = me.getCurrentPosition(),
            store = me.view.store;

        if (pos || pos === 0) {
            if (pos.isModel) {
                row = store.indexOf(pos);
                if (row !== -1) {
                    pos = {
                        row: row,
                        column: oldPos ? oldPos.column : 0
                    };
                } else {
                    pos = null;
                } 
            } else if (typeof pos === 'number') {
                pos = {
                    row: pos,
                    column: 0
                }
            }
        } 

        if (pos) {
            me.selectByPosition(pos, suppressEvent);   
        } else {
            me.deselect();
        }
    },

    deselect: function(record, suppressEvent){
        this.selectByPosition(null, suppressEvent);    
    },

    move: function(dir, e) {
        var me = this,
            pos = me.getCurrentPosition(),
            newPos;

        if (pos) {
            
            newPos = pos.view.walkCells(pos, dir, e, me.preventWrap);
            
            if (newPos) {
                return me.setCurrentPosition(newPos);
            }
        }
    },

    
    getCurrentPosition: function() {
        
        
        return this.selecting ? this.nextSelection : this.selection;
    },

    
    setCurrentPosition: function(pos, suppressEvent,  preventCheck) {
        var me = this,
            last = me.selection;

        
        me.lastSelection = last;

        
        if (pos) {
            pos = pos.isCellContext ? pos : new Ext.grid.CellContext(me.primaryView).setPosition(pos);
        }
        if (!preventCheck && last) {
            
            if (pos && (pos.record === last.record && pos.columnHeader === last.columnHeader && pos.view === last.view)) {
                pos = null;
            } else {
                me.onCellDeselect(me.selection, suppressEvent);
            }
        }

        if (pos) {
            me.nextSelection = pos;
            
            
            me.selecting = true;
            me.onCellSelect(me.nextSelection, suppressEvent);
            me.selecting = false;
            
            return (me.selection = pos);
        }
    },

    isCellSelected: function(view, row, column) {
        var me = this,
            testPos,
            pos = me.getCurrentPosition();

        if (pos && pos.view === view) {
            testPos = new Ext.grid.CellContext(view).setPosition({
                row: row,
                column: column
            });
            return (testPos.record === pos.record) && (testPos.columnHeader === pos.columnHeader);
        }
    },

    
    onStoreRemove: function(store, records, indexes) {
        var me = this,
            pos = me.getCurrentPosition();

        me.callParent(arguments);
        if (pos && store.getCount() && store.indexOf(pos.record) !== -1) {
            me.setCurrentPosition({
                row: pos.record,
                column: pos.columnHeader
            }, true, true);
        } else {
            me.selection = null;
        }
    },
    
    onStoreAdd: function() {
        var me = this,
            pos = me.getCurrentPosition();

        me.callParent(arguments);
        if (pos) {
            me.setCurrentPosition({
                row: pos.record,
                column: pos.columnHeader
            }, true, true);
        } else {
            me.selection = null;
        }
    },

    
    onCellClick: function(view, cell, cellIndex, record, row, recordIndex, e) {
        var newPos;

        
        if (recordIndex !== -1) {
            newPos = new Ext.grid.CellContext(view).setPosition({
                view: view,
                row: row,
                
                column: view.ownerCt.getColumnManager().getHeaderAtIndex(cellIndex)
            });
            this.setCurrentPosition(newPos);
        }
    },

    
    
    onCellSelect: function(position, supressEvent) {
        if (position && position.row !== undefined && position.row > -1) {
            this.doSelect(position.record, false, supressEvent);
        }
    },

    
    
    onCellDeselect: function(position, supressEvent) {
        if (position && position.row !== undefined) {
            this.doDeselect(position.record, supressEvent);
        }
    },

    onSelectChange: function(record, isSelected, suppressEvent, commitFn) {
        var me = this,
            pos,
            eventName,
            view;

        if (isSelected) {
            pos = me.nextSelection;
            eventName = 'select';
        } else {
            pos = me.lastSelection || me.noSelection;
            eventName = 'deselect';
        }

        
        
        
        view = pos.view || me.primaryView;

        if ((suppressEvent || me.fireEvent('before' + eventName, me, record, pos.row, pos.column)) !== false &&
                commitFn() !== false) {

            if (isSelected) {
                if (!me.preventFocus) {
                    view.focusCell(pos, true);
                }
                view.onCellSelect(pos);
            } else {
                view.onCellDeselect(pos);
                delete me.selection;
            }

            if (!suppressEvent) {
                me.fireEvent(eventName, me, record, pos.row, pos.column);
            }
        }
    },

    
    onKeyTab: function(e, t) {
        var me = this,
            pos = me.getCurrentPosition(),
            editingPlugin;

        if (pos) {
            editingPlugin = pos.view.editingPlugin;
            
            if (editingPlugin && me.wasEditing) {
                me.onEditorTab(editingPlugin, e)
            } else {
                me.move(e.shiftKey ? 'left' : 'right', e);
            }
        }
    },

    onEditorTab: function(editingPlugin, e) {
        var me = this,
            direction = e.shiftKey ? 'left' : 'right',
            position  = me.move(direction, e);

        
        if (position) {
            
            if (editingPlugin.startEdit(position.record, position.columnHeader)) {
                me.wasEditing = false;
            }
            
            
            
            else {
                position.view.scrollCellIntoView(position);
                me.wasEditing = true;
            }
        }
    },

    refresh: function() {
        var pos = this.getCurrentPosition(),
            selRowIdx;

        
        if (pos && (selRowIdx = this.store.indexOf(this.selected.last())) !== -1) {
            pos.row = selRowIdx;
        }
    },

    
    onColumnMove: function(headerCt, header, fromIdx, toIdx) {
        var grid = headerCt.up('tablepanel');
        if (grid) {
            this.onViewRefresh(grid.view);
        }
    },
    
    onUpdate: function(record) {
        var me = this,
            pos;
            
        if (me.isSelected(record)) {
            pos = me.selecting ? me.nextSelection : me.selection; 
            me.view.onCellSelect(pos);
        }
    },

    onViewRefresh: function(view) {
        var me = this,
            pos = me.getCurrentPosition(),
            newPos,
            headerCt = view.headerCt,
            record, columnHeader;

        
        
        if (pos && pos.view === view) {
            record = pos.record;
            columnHeader = pos.columnHeader;

            
            if (!columnHeader.isDescendantOf(headerCt)) {
                
                
                
                columnHeader = headerCt.queryById(columnHeader.id) || 
                               headerCt.down('[text="' + columnHeader.text + '"]') ||
                               headerCt.down('[dataIndex="' + columnHeader.dataIndex + '"]');
            }

            
            
            
            
            if (pos.record) {
                if (columnHeader && (view.store.indexOfId(record.getId()) !== -1)) {
                    newPos = new Ext.grid.CellContext(view).setPosition({
                        row: record,
                        column: columnHeader
                    });
                    me.setCurrentPosition(newPos);
                }
            } else {
                me.selection = null;
            }
        }
    },

    selectByPosition: function(position, suppressEvent) {
        this.setCurrentPosition(position, suppressEvent);
    }
});


Ext.define('Ext.selection.RowModel', {
    extend:  Ext.selection.Model ,
    alias: 'selection.rowmodel',
                                  

    
    deltaScroll: 5,

    
    enableKeyNav: true,

    
    ignoreRightMouseSelection: false,

    isRowModel: true,

    constructor: function() {
        this.addEvents(
            
            'beforedeselect',

            
            'beforeselect',

            
            'deselect',

            
            'select'
        );
        this.views = [];
        this.callParent(arguments);
    },

    bindComponent: function(view) {
        var me = this;

        view.on({
            
            
            
            itemcontextmenu: me.onRowClick,
            itemclick: me.onRowClick,
            scope: me
        });

        if (me.enableKeyNav) {
            me.initKeyNav(view);
        }
    },

    initKeyNav: function(view) {
        var me = this;

        if (!view.rendered) {
            view.on('render', Ext.Function.bind(me.initKeyNav, me, [view], 0), me, {single: true});
            return;
        }

        
        
        view.el.set({
            tabIndex: -1
        });

        
        me.keyNav = new Ext.util.KeyNav({
            target: view,
            ignoreInputFields: true,
            eventName: 'itemkeydown',
            processEvent: function(view, record, node, index, event) {
                event.record = record;
                event.recordIndex = index;
                return event;
            },
            up: me.onKeyUp,
            down: me.onKeyDown,
            right: me.onKeyRight,
            left: me.onKeyLeft,
            pageDown: me.onKeyPageDown,
            pageUp: me.onKeyPageUp,
            home: me.onKeyHome,
            end: me.onKeyEnd,
            space: me.onKeySpace,
            enter: me.onKeyEnter,
            scope: me
        });
    },

    onUpdate: function(record) {
        var me = this,
            view = me.view,
            index;

        if (view && me.isSelected(record)) {
            index = view.indexOf(record);
            view.onRowSelect(index);
            if (record === me.lastFocused) {
                view.onRowFocus(index, true);
            }
        }
    },

    
    
    
    getRowsVisible: function() {
        var rowsVisible = false,
            view = this.views[0],
            firstRow = view.all.first(),
            rowHeight, gridViewHeight;

        if (firstRow) {
            rowHeight = firstRow.getHeight();
            gridViewHeight = view.el.getHeight();
            rowsVisible = Math.floor(gridViewHeight / rowHeight);
        }

        return rowsVisible;
    },

    
    onKeyEnd: function(e) {
        var me = this,
            view = me.views[0];

        if (view.bufferedRenderer) {
            
            
            
            view.bufferedRenderer.scrollTo(me.store.getCount() - 1, false, function(newIdx, newRecord) {
                me.afterKeyNavigate(e, newRecord)
            });
        } else {
            me.afterKeyNavigate(e, view.getRecord(view.all.getCount() - 1))
        }
    },

    
    onKeyHome: function(e) {
        var me = this,
            view = me.views[0];

        if (view.bufferedRenderer) {
            
            
            
            view.bufferedRenderer.scrollTo(0, false, function(newIdx, newRecord) {
                me.afterKeyNavigate(e, newRecord)
            });
        } else {
            me.afterKeyNavigate(e, view.getRecord(0));
        }
    },

    
    onKeyPageUp: function(e) {
        var me = this,
            view = me.views[0],
            rowsVisible = me.getRowsVisible(),
            newIdx,
            newRecord;

        if (rowsVisible) {
            
            
            
            if (view.bufferedRenderer) {
                newIdx = Math.max(e.recordIndex - rowsVisible, 0);
                (me.lastKeyEvent || (me.lastKeyEvent = new Ext.EventObjectImpl())).setEvent(e.browserEvent);
                view.bufferedRenderer.scrollTo(newIdx, false, me.afterBufferedScrollTo, me);
            } else {
                newRecord = view.walkRecs(e.record, -rowsVisible);
                me.afterKeyNavigate(e, newRecord);
            }
        }
    },

    
    onKeyPageDown: function(e) {
        var me = this,
            view = me.views[0],
            rowsVisible = me.getRowsVisible(),
            newIdx,
            newRecord;

        if (rowsVisible) {
            
            
            
            if (view.bufferedRenderer) {
                newIdx = Math.min(e.recordIndex + rowsVisible, me.store.getCount() - 1);
                (me.lastKeyEvent || (me.lastKeyEvent = new Ext.EventObjectImpl())).setEvent(e.browserEvent);
                view.bufferedRenderer.scrollTo(newIdx, false, me.afterBufferedScrollTo, me);
            } else {
                newRecord = view.walkRecs(e.record, rowsVisible);
                me.afterKeyNavigate(e, newRecord);
            }
        }
    },

    
    onKeySpace: function(e) {
        var record = this.lastFocused;

        if (record) {
            this.afterKeyNavigate(e, record);
        }
    },

    onKeyEnter: Ext.emptyFn,

    
    
    
    onKeyUp: function(e) {
        var newRecord = this.views[0].walkRecs(e.record, -1);

        if (newRecord) {
            this.afterKeyNavigate(e, newRecord);
        }
    },

    
    
    
    onKeyDown: function(e) {
        
        
        var newRecord = e.record.isExpandingOrCollapsing ? null : this.views[0].walkRecs(e.record, 1);

        if (newRecord) {
            this.afterKeyNavigate(e, newRecord);
        }
    },

    afterBufferedScrollTo: function(newIdx, newRecord) {
        this.afterKeyNavigate(this.lastKeyEvent, newRecord)
    },

    scrollByDeltaX: function(delta) {
        var view    = this.views[0],
            section = view.up(),
            hScroll = section.horizontalScroller;

        if (hScroll) {
            hScroll.scrollByDeltaX(delta);
        }
    },

    onKeyLeft: function(e) {
        this.scrollByDeltaX(-this.deltaScroll);
    },

    onKeyRight: function(e) {
        this.scrollByDeltaX(this.deltaScroll);
    },

    
    
    onRowClick: function(view, record, item, index, e) {
        var me = this;

        
        if (index !== -1) {
            if (!me.allowRightMouseSelection(e)) {
                return;
            }

            me.processSelection(view, record, item, index, e);
        }
    },

    
    processSelection: function(view, record, item, index, e) {
        this.selectWithEvent(record, e);
    },

    
    allowRightMouseSelection: function(e) {
        var disallow = this.ignoreRightMouseSelection && e.button !== 0;
        if (disallow) {
            disallow = this.hasSelection();
        }
        return !disallow;
    },

    
    
    onSelectChange: function(record, isSelected, suppressEvent, commitFn) {
        var me      = this,
            views   = me.views,
            viewsLn = views.length,
            rowIdx  = views[0].indexOf(record),
            eventName = isSelected ? 'select' : 'deselect',
            i = 0;

        if ((suppressEvent || me.fireEvent('before' + eventName, me, record, rowIdx)) !== false &&
                commitFn() !== false) {

            for (; i < viewsLn; i++) {
                if (isSelected) {
                    views[i].onRowSelect(rowIdx, suppressEvent);
                } else {
                    views[i].onRowDeselect(rowIdx, suppressEvent);
                }
            }

            if (!suppressEvent) {
                me.fireEvent(eventName, me, record, rowIdx);
            }
        }
    },

    
    
    onLastFocusChanged: function(oldFocused, newFocused, supressFocus) {
        var views   = this.views,
            viewsLn = views.length,
            rowIdx,
            i = 0;

        if (oldFocused) {
            rowIdx = views[0].indexOf(oldFocused);
            if (rowIdx != -1) {
                for (; i < viewsLn; i++) {
                    views[i].onRowFocus(rowIdx, false, true);
                }
            }
        }

        if (newFocused) {
            rowIdx = views[0].indexOf(newFocused);
            if (rowIdx != -1) {
                for (i = 0; i < viewsLn; i++) {
                    views[i].onRowFocus(rowIdx, true, supressFocus);
                }
            }
        }
        this.callParent(arguments);
    },

    onEditorTab: function(editingPlugin, e) {
        var me = this,
            view = editingPlugin.context.view,
            record = editingPlugin.getActiveRecord(),
            header = editingPlugin.getActiveColumn(),
            position = view.getPosition(record, header),
            direction = e.shiftKey ? 'left' : 'right',
            lastPos;

        
        
        
        

        do {
            lastPos = position;
            position  = view.walkCells(position, direction, e, me.preventWrap);
            if (lastPos && lastPos.isEqual(position)) {
                
                
                
                return;
            }
        } while (position && (!position.columnHeader.getEditor(record) || !editingPlugin.startEditByPosition(position)));
    },

    
    getCurrentPosition: function() {
        var firstSelection = this.selected.getAt(0);
        if (firstSelection) {
            return new Ext.grid.CellContext(this.view).setPosition(this.store.indexOf(firstSelection), 0);
        }
    },

    selectByPosition: function(position) {
        var context = new Ext.grid.CellContext(this.view);
            
        context.setPosition(position.row, position.column);
        this.select(context.record);
    },

    
    selectNext: function(keepExisting, suppressEvent) {
        var me = this,
            store = me.store,
            selection = me.getSelection(),
            record = selection[selection.length - 1],
            index = me.views[0].indexOf(record) + 1,
            success;

        if (index === store.getCount() || index === 0) {
            success = false;
        } else {
            me.doSelect(index, keepExisting, suppressEvent);
            success = true;
        }
        return success;
    },

    
    selectPrevious: function(keepExisting, suppressEvent) {
        var me = this,
            selection = me.getSelection(),
            record = selection[0],
            index = me.views[0].indexOf(record) - 1,
            success;

        if (index < 0) {
            success = false;
        } else {
            me.doSelect(index, keepExisting, suppressEvent);
            success = true;
        }
        return success;
    },

    isRowSelected: function(record) {
        return this.isSelected(record);
    },

    isCellSelected: function(view, record, columnHeader) {
        return this.isSelected(record);
    } 
});


Ext.define('Ext.selection.TreeModel', {
    extend:  Ext.selection.RowModel ,
    alias: 'selection.treemodel',

    

    constructor: function(config) {
        this.callParent(arguments);

        
        
        if (this.pruneRemoved) {
            this.pruneRemoved = false;
            this.pruneRemovedNodes = true;
        }
    },

    
    bindStore: function(store, initial) {
        var me = this;
        me.callParent(arguments);

        
        
        if (me.pruneRemovedNodes) {
            me.view.mon(me.treeStore, {
                remove: me.onNodeRemove,
                scope: me
            });
        }
    },

    onNodeRemove: function(parent, node, isMove) {
        
        if (!isMove) {
            this.deselectDeletedRecords([node]);
        }
    },

    onKeyRight: function(e, t) {
        this.navExpand(e, t);
    },
    
    navExpand: function(e, t) {
        var focused = this.getLastFocused(),
            view    = this.view;

        if (focused) {
            
            
            
            if (focused.isExpanded()) {
                this.onKeyDown(e, t);
            
            } else if (focused.isExpandable()) {
                
                if (!view.isTreeView) {
                    view = view.lockingPartner;
                }

                view.expand(focused);
            }
        }
    },

    onKeyLeft: function(e, t) {
        this.navCollapse(e, t);
    },
    
    navCollapse: function(e, t) {
        var me = this,
            focused = this.getLastFocused(),
            view    = this.view,
            parentNode;

        if (focused) {
            parentNode = focused.parentNode;
            
            if (focused.isExpanded()) {
                
                if (!view.isTreeView) {
                    view = view.lockingPartner;
                }

                view.collapse(focused);
            
            
            } else if (parentNode && !parentNode.isRoot()) {
                
                if (e.shiftKey) {
                    me.selectRange(parentNode, focused, e.ctrlKey, 'up');
                    me.setLastFocused(parentNode);
                
                } else if (e.ctrlKey) {
                    me.setLastFocused(parentNode);
                
                } else {
                    me.select(parentNode);
                }
            }
        }
    },

    onKeySpace: function(e, t) {
        if (e.record.data.checked != null) {
            this.toggleCheck(e);
        } else {
            this.callParent(arguments);
        }
    },

    onKeyEnter: function(e, t) {
        if (e.record.data.checked != null) {
            this.toggleCheck(e);
        } else {
            this.callParent(arguments);
        }
    },

    toggleCheck: function(e) {
        var view = this.view,
            selected = this.getLastSelected();

        e.stopEvent();
        if (selected) {
            
            if (!view.isTreeView) {
                view = view.lockingPartner;
            }

            view.onCheckChange(selected);
        }
    }
});


Ext.define('Ext.slider.Thumb', {
                                                        
    
    topZIndex: 10000,

    

    
    constructor: function(config) {
        var me = this;

        
        Ext.apply(me, config || {}, {
            cls: Ext.baseCSSPrefix + 'slider-thumb',

            
            constrain: false
        });
        me.callParent([config]);
    },

    
    render: function() {
        var me = this;
        me.el = me.slider.innerEl.insertFirst(me.getElConfig());
        me.onRender();
    },
    
    onRender: function() {
        if (this.disabled) {
            this.disable();
        }
        this.initEvents();
    },

    getElConfig: function() {
        var me = this,
            slider = me.slider,
            style = {};

        style[slider.vertical ? 'bottom' : slider.horizontalProp] = slider.calculateThumbPosition(slider.normalizeValue(me.value)) + '%';
        return {
            style: style,
            id  : this.id,
            cls : this.cls,
            role: 'presentation'
        };
    },

    
    move: function(v, animate) {
        var me = this,
            el = me.el,
            slider = me.slider,
            styleProp = slider.vertical ? 'bottom' : slider.horizontalProp,
            to,
            from;

        v += '%';
        
        if (!animate) {
            el.dom.style[styleProp] = v;
        } else {
            to = {};
            to[styleProp] = v;
            
            if (!Ext.supports.GetPositionPercentage) {
                from = {};
                from[styleProp] = el.dom.style[styleProp];
            }
            
            new Ext.fx.Anim({
                target: el,
                duration: 350,
                from: from,
                to: to
            });
        }
    },

    
    bringToFront: function() {
        this.el.setStyle('zIndex', this.topZIndex);
    },

    
    sendToBack: function() {
        this.el.setStyle('zIndex', '');
    },

    
    enable: function() {
        var me = this;

        me.disabled = false;
        if (me.el) {
            me.el.removeCls(me.slider.disabledCls);
        }
    },

    
    disable: function() {
        var me = this;

        me.disabled = true;
        if (me.el) {
            me.el.addCls(me.slider.disabledCls);
        }
    },

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

        me.tracker = new Ext.dd.DragTracker({
            onBeforeStart: Ext.Function.bind(me.onBeforeDragStart, me),
            onStart      : Ext.Function.bind(me.onDragStart, me),
            onDrag       : Ext.Function.bind(me.onDrag, me),
            onEnd        : Ext.Function.bind(me.onDragEnd, me),
            tolerance    : 3,
            autoStart    : 300,
            overCls      : Ext.baseCSSPrefix + 'slider-thumb-over'
        });

        me.tracker.initEl(el);
    },

    
    onBeforeDragStart : function(e) {
        if (this.disabled) {
            return false;
        } else {
            this.slider.promoteThumb(this);
            return true;
        }
    },

    
    onDragStart: function(e){
        var me = this,
            slider = me.slider;

        slider.onDragStart(me, e);
        me.el.addCls(Ext.baseCSSPrefix + 'slider-thumb-drag');
        me.dragging = me.slider.dragging = true;
        me.dragStartValue = me.value;

        slider.fireEvent('dragstart', slider, e, me);
    },

    
    onDrag: function(e) {
        var me       = this,
            slider   = me.slider,
            index    = me.index,
            newValue = me.getValueFromTracker(),
            above,
            below;

        
        if (newValue !== undefined) {
            if (me.constrain) {
                above = slider.thumbs[index + 1];
                below = slider.thumbs[index - 1];

                if (below !== undefined && newValue <= below.value) {
                    newValue = below.value;
                }

                if (above !== undefined && newValue >= above.value) {
                    newValue = above.value;
                }
            }
            slider.setValue(index, newValue, false);
            slider.fireEvent('drag', slider, e, me);
        }
    },

    getValueFromTracker: function() {
        var slider = this.slider,
            trackPoint = slider.getTrackpoint(this.tracker.getXY());

        
        if (trackPoint !== undefined) {
            return slider.reversePixelValue(trackPoint);
        }
    },

    
    onDragEnd: function(e) {
        var me     = this,
            slider = me.slider,
            value  = me.value;

        slider.onDragEnd(me, e);
        me.el.removeCls(Ext.baseCSSPrefix + 'slider-thumb-drag');

        me.dragging = slider.dragging = false;
        slider.fireEvent('dragend', slider, e);

        if (me.dragStartValue != value) {
            slider.fireEvent('changecomplete', slider, value, me);
        }
    },

    destroy: function() {
        Ext.destroy(this.tracker);
    }
});


Ext.define('Ext.slider.Tip', {
    extend:  Ext.tip.Tip ,
    minWidth: 10,
    alias: 'widget.slidertip',
    
    
    offsets : null,
    
    
    align: null,
    
    
    position: '',
    
    defaultVerticalPosition: 'left',
    
    defaultHorizontalPosition: 'top',

    isSliderTip: true,

    init: function(slider) {
        var me = this,
            align,
            offsets;
        
        if (!me.position) {
            me.position = slider.vertical ? me.defaultVerticalPosition : me.defaultHorizontalPosition;
        }
            
        switch (me.position) {
            case 'top':
                offsets = [0, -10];
                align = 'b-t?';
                break;
            case 'bottom':
                offsets = [0, 10];
                align = 't-b?';
                break;
            case 'left':
                offsets = [-10, 0];
                align = 'r-l?';
                break;
            case 'right':
                offsets = [10, 0];
                align = 'l-r?';
        }
        
        if (!me.align) {
            me.align = align;
        }
        
        if (!me.offsets) {
            me.offsets = offsets;
        }

        slider.on({
            scope    : me,
            dragstart: me.onSlide,
            drag     : me.onSlide,
            dragend  : me.hide,
            destroy  : me.destroy
        });
    },
    
    onSlide : function(slider, e, thumb) {
        var me = this;
        me.show();
        me.update(me.getText(thumb));
        me.el.alignTo(thumb.el, me.align, me.offsets);
    },

    
    getText : function(thumb) {
        return String(thumb.value);
    }
});


Ext.define('Ext.slider.Multi', {
    extend:  Ext.form.field.Base ,
    alias: 'widget.multislider',
    alternateClassName: 'Ext.slider.MultiSlider',

               
                           
                         
                     
                          
                       
                                           
      

    childEls: [
        'endEl', 'innerEl'
    ],

    
    fieldSubTpl: [
        '<div id="{id}" role="{role}" {inputAttrTpl} class="' + Ext.baseCSSPrefix + 'slider {fieldCls} {vertical}',
        '{childElCls}"',
        '<tpl if="tabIdx != null"> tabIndex="{tabIdx}"</tpl>',
        '<tpl if="isVertical"> aria-orientation="vertical"<tpl else> aria-orientation="horizontal"</tpl>',
        '>',
            '<div id="{cmpId}-endEl" class="' + Ext.baseCSSPrefix + 'slider-end" role="presentation">',
                '<div id="{cmpId}-innerEl" class="' + Ext.baseCSSPrefix + 'slider-inner" role="presentation">',
                    '{%this.renderThumbs(out, values)%}',
                '</div>',
            '</div>',
        '</div>',
        {
            renderThumbs: function(out, values) {
                var me = values.$comp,
                    i = 0,
                    thumbs = me.thumbs,
                    len = thumbs.length,
                    thumb,
                    thumbConfig;

                for (; i < len; i++) {
                    thumb = thumbs[i];
                    thumbConfig = thumb.getElConfig();
                    thumbConfig.id = me.id + '-thumb-' + i;
                    Ext.DomHelper.generateMarkup(thumbConfig, out);
                }
            },
            disableFormats: true
        }
    ],
    
    horizontalProp: 'left',

    

    

    
    vertical: false,

    
    minValue: 0,

    
    maxValue: 100,

    
    decimalPrecision: 0,

    
    keyIncrement: 1,

    
    increment: 0,

    

    
    clickRange: [5,15],

    
    clickToChange : true,

    
    animate: true,

    
    dragging: false,

    
    constrainThumbs: true,

    componentLayout: 'sliderfield',

    
    useTips : true,

    
    tipText : null,

    ariaRole: 'slider',

    
    initValue: function() {
        var me = this,
            extValue = Ext.value,
            
            values = extValue(me.values, [extValue(me.value, extValue(me.minValue, 0))]),
            i = 0,
            len = values.length;

        
        me.originalValue = values;

        
        for (; i < len; i++) {
            me.addThumb(values[i]);
        }
    },

    
    initComponent : function() {
        var me = this,
            tipPlug,
            hasTip,
            p, pLen, plugins;

        
        me.thumbs = [];

        me.keyIncrement = Math.max(me.increment, me.keyIncrement);

        me.addEvents(
            
            'beforechange',

            
            'change',

            
            'changecomplete',

            
            'dragstart',

            
            'drag',

            
            'dragend'
        );

        me.callParent();

        
        if (me.useTips) {
            if (Ext.isObject(me.useTips)) {
                tipPlug = Ext.apply({}, me.useTips);
            } else {
                tipPlug = me.tipText ? {getText: me.tipText} : {};    
            }

            plugins = me.plugins = me.plugins || [];
            pLen    = plugins.length;
            
            for (p = 0; p < pLen; p++) {
                if (plugins[p].isSliderTip) {
                    hasTip = true;
                    break;
                }
            }

            if (!hasTip) {
                me.plugins.push(new Ext.slider.Tip(tipPlug));
            }
        }
    },

    
    addThumb: function(value) {
        var me = this,
            thumb = new Ext.slider.Thumb({
                ownerCt     : me,
                ownerLayout : me.getComponentLayout(),
                value       : value,
                slider      : me,
                index       : me.thumbs.length,
                constrain   : me.constrainThumbs,
                disabled    : !!me.readOnly
            });

        me.thumbs.push(thumb);

        
        if (me.rendered) {
            thumb.render();
        }

        return thumb;
    },

    
    promoteThumb: function(topThumb) {
        var thumbs = this.thumbs,
            ln = thumbs.length,
            zIndex, thumb, i;

        for (i = 0; i < ln; i++) {
            thumb = thumbs[i];

            if (thumb == topThumb) {
                thumb.bringToFront();
            } else {
                thumb.sendToBack();
            }
        }
    },

    
    getSubTplData : function() {
        var me = this;

        return Ext.apply(me.callParent(), {
            $comp: me,
            isVertical: me.vertical,
            vertical: me.vertical ? Ext.baseCSSPrefix + 'slider-vert' : Ext.baseCSSPrefix + 'slider-horz',
            minValue: me.minValue,
            maxValue: me.maxValue,
            value: me.value,
            tabIdx: me.tabIndex,
            childElCls: ''
        });
    },

    onRender : function() {
        var me = this,
            thumbs = me.thumbs,
            len = thumbs.length,
            i = 0,
            thumb;

        me.callParent(arguments);

        for (i = 0; i < len; i++) {
            thumb = thumbs[i];
            thumb.el = me.el.getById(me.id + '-thumb-' + i);
            thumb.onRender();
        }
    },

    
    initEvents : function() {
        var me = this;
        me.mon(me.el, {
            scope    : me,
            mousedown: me.onMouseDown,
            keydown  : me.onKeyDown
        });
    },
    
    onDragStart: Ext.emptyFn,
    onDragEnd: Ext.emptyFn,

    
    getTrackpoint : function(xy) {
        var me = this,
            vertical = me.vertical,
            sliderTrack = me.innerEl,
            trackLength, result,
            positionProperty;

        if (vertical) {
            positionProperty = 'top';
            trackLength = sliderTrack.getHeight();
        } else {
            positionProperty = me.horizontalProp;
            trackLength = sliderTrack.getWidth();
        }
        xy = me.transformTrackPoints(sliderTrack.translatePoints(xy));
        result = Ext.Number.constrain(xy[positionProperty], 0, trackLength);
        return vertical ? trackLength - result : result;
    },
    
    transformTrackPoints: Ext.identityFn,

    
    onMouseDown : function(e) {
        var me = this,
            thumbClicked = false,
            i = 0,
            thumbs = me.thumbs,
            len = thumbs.length,
            trackPoint;

        if (me.disabled) {
            return;
        }

        
        for (; i < len; i++) {
            thumbClicked = thumbClicked || e.target == thumbs[i].el.dom;
        }

        if (me.clickToChange && !thumbClicked) {
            trackPoint = me.getTrackpoint(e.getXY());
            if (trackPoint !== undefined) {
                me.onClickChange(trackPoint);
            }
        }
        me.focus();
    },

    
    onClickChange : function(trackPoint) {
        var me = this,
            thumb, index;

        
        

        
        thumb = me.getNearest(trackPoint);
        if (!thumb.disabled) {
            index = thumb.index;
            me.setValue(index, Ext.util.Format.round(me.reversePixelValue(trackPoint), me.decimalPrecision), undefined, true);
        }
    },

    
    getNearest: function(trackPoint) {
        var me = this,
            clickValue = me.reversePixelValue(trackPoint),
            nearestDistance = me.getRange() + 5, 
            nearest = null,
            thumbs = me.thumbs,
            i = 0,
            len = thumbs.length,
            thumb,
            value,
            dist;

        for (; i < len; i++) {
            thumb = me.thumbs[i];
            value = thumb.value;
            dist  = Math.abs(value - clickValue);

            if (Math.abs(dist <= nearestDistance)) {
                nearest = thumb;
                nearestDistance = dist;
            }
        }
        return nearest;
    },

    
    onKeyDown : function(e) {
        
        var me = this,
            k,
            val;

        if(me.disabled || me.thumbs.length !== 1) {
            e.preventDefault();
            return;
        }
        k = e.getKey();

        switch(k) {
            case e.UP:
            case e.RIGHT:
                e.stopEvent();
                val = e.ctrlKey ? me.maxValue : me.getValue(0) + me.keyIncrement;
                me.setValue(0, val, undefined, true);
            break;
            case e.DOWN:
            case e.LEFT:
                e.stopEvent();
                val = e.ctrlKey ? me.minValue : me.getValue(0) - me.keyIncrement;
                me.setValue(0, val, undefined, true);
            break;
            default:
                e.preventDefault();
        }
    },

    
    normalizeValue : function(v) {
        var me = this,
            snapFn = me.zeroBasedSnapping ? 'snap' : 'snapInRange';

        v = Ext.Number[snapFn](v, me.increment, me.minValue, me.maxValue);
        v = Ext.util.Format.round(v, me.decimalPrecision);
        v = Ext.Number.constrain(v, me.minValue, me.maxValue);
        return v;
    },

    
    setMinValue : function(val) {
        var me = this,
            thumbs = me.thumbs,
            len = thumbs.length,
            thumb, i;

        me.minValue = val;

        for (i = 0; i < len; ++i) {
            thumb = thumbs[i];
            if (thumb.value < val) {
                me.setValue(i, val, false);    
            }
        }
        me.syncThumbs();
    },

    
    setMaxValue : function(val) {
        var me = this,
            thumbs = me.thumbs,
            len = thumbs.length,
            thumb, i;

        me.maxValue = val;

        for (i = 0; i < len; ++i) {
            thumb = thumbs[i];
            if (thumb.value > val) {
                me.setValue(i, val, false);
            }
        }
        me.syncThumbs();
    },

    
    setValue : function(index, value, animate, changeComplete) {
        var me = this,
            thumbs = me.thumbs,
            thumb, len, i, values;
            
        if (Ext.isArray(index)) {
            values = index;
            animate = value;
            
            for (i = 0, len = values.length; i < len; ++i) {
                thumb = thumbs[i];
                if (thumb) {
                    me.setValue(i, values[i], animate);
                }    
            }
            return me;
        }

        thumb = me.thumbs[index];
        
        value = me.normalizeValue(value);

        if (value !== thumb.value && me.fireEvent('beforechange', me, value, thumb.value, thumb) !== false) {
            thumb.value = value;
            if (me.rendered) {
                thumb.move(me.calculateThumbPosition(value), Ext.isDefined(animate) ? animate !== false : me.animate);

                me.fireEvent('change', me, value, thumb);
                me.checkDirty();
                if (changeComplete) {
                    me.fireEvent('changecomplete', me, value, thumb);
                }
            }
        }
        return me;
    },

    
    calculateThumbPosition : function(v) {
        var me = this,
            minValue = me.minValue,
            pos = (v - minValue) / me.getRange() * 100;

        
        if (isNaN(pos)) {
            pos = minValue;
        }

        return pos;
    },

    
    getRatio : function() {
        var me = this,
            innerEl = me.innerEl,
            trackLength = me.vertical ? innerEl.getHeight() : innerEl.getWidth(),
            valueRange = me.getRange();
            
        return valueRange === 0 ? trackLength : (trackLength / valueRange);
    },
    
    getRange: function(){
        return this.maxValue - this.minValue;
    },

    
    reversePixelValue : function(pos) {
        return this.minValue + (pos / this.getRatio());
    },

    
    reversePercentageValue : function(pos) {
        return this.minValue + this.getRange() * (pos / 100);
    },

    
    onDisable: function() {
        var me = this,
            i = 0,
            thumbs = me.thumbs,
            len = thumbs.length,
            thumb,
            el,
            xy;

        me.callParent();

        for (; i < len; i++) {
            thumb = thumbs[i];
            el = thumb.el;

            thumb.disable();

            if(Ext.isIE) {
                
                
                xy = el.getXY();
                el.hide();

                me.innerEl.addCls(me.disabledCls).dom.disabled = true;

                if (!me.thumbHolder) {
                    me.thumbHolder = me.endEl.createChild({
                        role: 'presentation',
                        cls: Ext.baseCSSPrefix + 'slider-thumb ' + me.disabledCls
                    });
                }

                me.thumbHolder.show().setXY(xy);
            }
        }
    },

    
    onEnable: function() {
        var me = this,
            i = 0,
            thumbs = me.thumbs,
            len = thumbs.length,
            thumb,
            el;

        this.callParent();

        for (; i < len; i++) {
            thumb = thumbs[i];
            el = thumb.el;

            thumb.enable();

            if (Ext.isIE) {
                me.innerEl.removeCls(me.disabledCls).dom.disabled = false;

                if (me.thumbHolder) {
                    me.thumbHolder.hide();
                }

                el.show();
                me.syncThumbs();
            }
        }
    },

    
    syncThumbs : function() {
        if (this.rendered) {
            var thumbs = this.thumbs,
                length = thumbs.length,
                i = 0;

            for (; i < length; i++) {
                thumbs[i].move(this.calculateThumbPosition(thumbs[i].value));
            }
        }
    },

    
    getValue : function(index) {
        return Ext.isNumber(index) ? this.thumbs[index].value : this.getValues();
    },

    
    getValues: function() {
        var values = [],
            i = 0,
            thumbs = this.thumbs,
            len = thumbs.length;

        for (; i < len; i++) {
            values.push(thumbs[i].value);
        }

        return values;
    },

    getSubmitValue: function() {
        var me = this;
        return (me.disabled || !me.submitValue) ? null : me.getValue();
    },

    reset: function() {
        var me = this,
            arr = [].concat(me.originalValue),
            a     = 0,
            aLen  = arr.length,
            val;

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

            me.setValue(a, val);
        }

        me.clearInvalid();
        
        delete me.wasValid;
    },
    
    setReadOnly: function(readOnly){
        var me = this,
            thumbs = me.thumbs,
            len = thumbs.length,
            i = 0;
            
        me.callParent(arguments); 
        readOnly = me.readOnly;
        
        for (; i < len; ++i) {
            if (readOnly) {
                thumbs[i].disable();
            } else {
                thumbs[i].enable();
            }
            
        }
           
    },

    
    beforeDestroy : function() {
        var me     = this,
            thumbs = me.thumbs,
            t      = 0,
            tLen   = thumbs.length,
            thumb;

        Ext.destroy(me.innerEl, me.endEl, me.focusEl);

        for (; t < tLen; t++) {
            thumb = thumbs[t];

            Ext.destroy(thumb);
        }

        me.callParent();
    }
});


Ext.define('Ext.tab.Tab', {
    extend:  Ext.button.Button ,
    alias: 'widget.tab',

               
                         
      

    
    isTab: true,

    baseCls: Ext.baseCSSPrefix + 'tab',
    closeElOverCls: Ext.baseCSSPrefix + 'tab-close-btn-over',

    
    activeCls: 'active',

    

    
    closableCls: 'closable',

    
    closable: true,

    
    
    closeText: 'Close Tab',
    

    
    active: false,

    

    childEls: [
        'closeEl'
    ],

    scale: false,

    position: 'top',
    
    ariaRole: 'tab',
    
    initComponent: function() {
        var me = this;
        
        me.addEvents(
            
            'activate',

            
            'deactivate',

            
            'beforeclose',

            
            'close'
        );

        me.callParent(arguments);

        if (me.card) {
            me.setCard(me.card);
        }

        me.overCls = ['over', me.position + '-over'];
    },

    getTemplateArgs: function() {
        var me = this,
            result = me.callParent();

        result.closable = me.closable;
        result.closeText = me.closeText;

        return result;
    },
    
    getFramingInfoCls: function(){
        return this.baseCls + '-' + this.ui + '-' + this.position;
    },

    beforeRender: function() {
        var me = this,
            tabBar = me.up('tabbar'),
            tabPanel = me.up('tabpanel');
        
        me.callParent();
        
        me.addClsWithUI(me.position);

        if (me.active) {
            me.addClsWithUI([me.activeCls, me.position + '-' + me.activeCls]);
        }

        
        

        me.syncClosableUI();

        
        if (!me.minWidth) {
            me.minWidth = (tabBar) ? tabBar.minTabWidth : me.minWidth;
            if (!me.minWidth && tabPanel) {
                me.minWidth = tabPanel.minTabWidth;
            }
            if (me.minWidth && me.iconCls) {
                me.minWidth += 25;
            }
        }
        if (!me.maxWidth) {
            me.maxWidth = (tabBar) ? tabBar.maxTabWidth : me.maxWidth;
            if (!me.maxWidth && tabPanel) {
                me.maxWidth = tabPanel.maxTabWidth;
            }
        }
    },

    onRender: function() {
        var me = this;

        me.setElOrientation();

        me.callParent(arguments);

        if (me.closable) {
            me.closeEl.addClsOnOver(me.closeElOverCls);
        }
        
        me.initKeyNav();
    },
    
    initKeyNav: function() {
        var me = this;

        me.keyNav = new Ext.util.KeyNav(me.el, {
            enter: me.onEnterKey,
            del: me.onDeleteKey,
            scope: me
        });
    },

    setElOrientation: function() {
        var position = this.position;

        if (position === 'left' || position === 'right') {
            this.el.setVertical(position === 'right' ? 90 : 270);
        }
    },

    
    enable : function(silent) {
        var me = this;

        me.callParent(arguments);

        me.removeClsWithUI(me.position + '-disabled');

        return me;
    },

    
    disable : function(silent) {
        var me = this;

        me.callParent(arguments);

        me.addClsWithUI(me.position + '-disabled');

        return me;
    },

    onDestroy: function() {
        var me = this;

        Ext.destroy(me.keyNav);
        delete me.keyNav;

        me.callParent(arguments);
    },

    
    setClosable: function(closable) {
        var me = this;

        
        closable = (!arguments.length || !!closable);

        if (me.closable != closable) {
            me.closable = closable;

            
            if (me.card) {
                me.card.closable = closable;
            }

            me.syncClosableUI();

            if (me.rendered) {
                me.syncClosableElements();

                
                me.updateLayout();
            }
        }
    },

    
    syncClosableElements: function () {
        var me = this,
            closeEl = me.closeEl;

        if (me.closable) {
            if (!closeEl) {
                closeEl = me.closeEl = me.btnWrap.insertSibling({
                    tag: 'a',
                    role: 'presentation',
                    cls: me.baseCls + '-close-btn',
                    href: '#',
                    title: me.closeText
                }, 'after');
            }
            closeEl.addClsOnOver(me.closeElOverCls);
        } else if (closeEl) {
            closeEl.remove();
            delete me.closeEl;
        }
    },

    
    syncClosableUI: function () {
        var me = this,
            classes = [me.closableCls, me.closableCls + '-' + me.position];

        if (me.closable) {
            me.addClsWithUI(classes);
        } else {
            me.removeClsWithUI(classes);
        }
    },

    
    setCard: function(card) {
        var me = this;

        me.card = card;
        me.setText(me.title || card.title);
        me.setIconCls(me.iconCls || card.iconCls);
        me.setIcon(me.icon || card.icon);
        me.setGlyph(me.glyph || card.glyph);
    },

    
    onCloseClick: function() {
        var me = this;

        if (me.fireEvent('beforeclose', me) !== false) {
            if (me.tabBar) {
                if (me.tabBar.closeTab(me) === false) {
                    
                    return;
                }
            } else {
                
                me.fireClose();
            }
        }
    },

    
    fireClose: function(){
        this.fireEvent('close', this);
    },

    
    onEnterKey: function(e) {
        var me = this;

        if (me.tabBar) {
            me.tabBar.onClick(e, me.el);
        }
    },

   
    onDeleteKey: function(e) {
        if (this.closable) {
            this.onCloseClick();
        }
    },

    
    activate : function(supressEvent) {
        var me = this;

        me.active = true;
        me.addClsWithUI([me.activeCls, me.position + '-' + me.activeCls]);

        if (supressEvent !== true) {
            me.fireEvent('activate', me);
        }
    },

    
    deactivate : function(supressEvent) {
        var me = this;

        me.active = false;
        me.removeClsWithUI([me.activeCls, me.position + '-' + me.activeCls]);

        if (supressEvent !== true) {
            me.fireEvent('deactivate', me);
        }
    }
});


Ext.define('Ext.util.Point', {

    
    extend:  Ext.util.Region ,

    statics: {

        
        fromEvent: function(e) {
            e = e.browserEvent || e;
            e = (e.changedTouches && e.changedTouches.length > 0) ? e.changedTouches[0] : e;
            return new this(e.pageX, e.pageY);
        }
    },

    

    
    constructor: function(x, y) {
        this.callParent([y, x, y, x]);
    },

    
    toString: function() {
        return "Point[" + this.x + "," + this.y + "]";
    },

    
    equals: function(p) {
        return (this.x == p.x && this.y == p.y);
    },

    
    isWithin: function(p, threshold) {
        if (!Ext.isObject(threshold)) {
            threshold = {
                x: threshold,
                y: threshold
            };
        }

        return (this.x <= p.x + threshold.x && this.x >= p.x - threshold.x &&
                this.y <= p.y + threshold.y && this.y >= p.y - threshold.y);
    },

    
    isContainedBy: function(region) {
        if (!(region instanceof Ext.util.Region)) {
            region = Ext.get(region.el || region).getRegion();
        }
        return region.contains(this);
    },

    
    roundedEquals: function(p) {
        return (Math.round(this.x) == Math.round(p.x) && Math.round(this.y) == Math.round(p.y));
    }
}, function() {
    
    this.prototype.translate = Ext.util.Region.prototype.translateBy;
});


Ext.define('Ext.tab.Bar', {
    extend:  Ext.panel.Header ,
    alias: 'widget.tabbar',
    baseCls: Ext.baseCSSPrefix + 'tab-bar',

               
                      
                        
      

    
    isTabBar: true,
    
    
    
    

    
    defaultType: 'tab',

    
    plain: false,
    
    ariaRole: 'tablist',

    childEls: [
        'body', 'strip'
    ],

    
    renderTpl: [
        '<div id="{id}-body" role="presentation" class="{baseCls}-body {bodyCls} {bodyTargetCls}{childElCls}',
            '<tpl if="ui"> {baseCls}-body-{ui}',
                '<tpl for="uiCls"> {parent.baseCls}-body-{parent.ui}-{.}</tpl>',
            '</tpl>"<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>>',
            '{%this.renderContainer(out,values)%}',
        '</div>',
        '<div id="{id}-strip" role="presentation" class="{baseCls}-strip {baseCls}-strip-{dock}{childElCls}',
            '<tpl if="ui"> {baseCls}-strip-{ui}',
                '<tpl for="uiCls"> {parent.baseCls}-strip-{parent.ui}-{.}</tpl>',
            '</tpl>">',
        '</div>'
    ],

    

    

    _reverseDockNames: {
        left: 'right',
        right: 'left'
    },

    
    initComponent: function() {
        var me = this;

        if (me.plain) {
            me.addCls(me.baseCls + '-plain');
        }

        me.addClsWithUI(me.orientation);

        me.addEvents(
            
            'change'
        );

        
        me.callParent(arguments);
        Ext.merge(me.layout, me.initialConfig.layout);

        
        me.layout.align = (me.orientation == 'vertical') ? 'left' : 'top';
        me.layout.overflowHandler = new Ext.layout.container.boxOverflow.Scroller(me.layout);

        me.remove(me.titleCmp);
        delete me.titleCmp;

        Ext.apply(me.renderData, {
            bodyCls: me.bodyCls,
            dock: me.dock
        });
    },

    onRender: function() {
        var me = this;

        me.callParent();

        if (me.orientation === 'vertical' && (Ext.isIE8 || Ext.isIE9) && Ext.isStrict) {
            me.el.on({
                mousemove: me.onMouseMove, 
                scope: me
            });
        }
    },

    afterRender: function() {
        var layout = this.layout;

        this.callParent();
        if (Ext.isIE9 && Ext.isStrict && this.orientation === 'vertical') {
            
            
            layout.innerCt.on('scroll', function() {
                layout.innerCt.dom.scrollLeft = 0;
            });
        }
    },

    afterLayout: function() {
        this.adjustTabPositions();
        this.callParent(arguments);
    },

    adjustTabPositions: function() {
        var items = this.items.items,
            i = items.length,
            tab;

        
        
        
        
        
        if (!Ext.isIE9m) {
            if (this.dock === 'right') {
                
                
                while (i--) {
                    tab = items[i];
                    if (tab.isVisible()) {
                        tab.el.setStyle('left', tab.lastBox.width + 'px');
                    }
                }
            } else if (this.dock === 'left') {
                
                
                while (i--) {
                    tab = items[i];
                    if (tab.isVisible()) {
                        tab.el.setStyle('left', -tab.lastBox.height + 'px');
                    }
                }
            }
        }
    },

    getLayout: function() {
        var me = this;
        me.layout.type = (me.orientation === 'horizontal') ? 'hbox' : 'vbox';
        return me.callParent(arguments);
    },

    
    onAdd: function(tab) {
        tab.position = this.dock;
        this.callParent(arguments);
    },
    
    onRemove: function(tab) {
        var me = this;
        
        if (tab === me.previousTab) {
            me.previousTab = null;
        }
        me.callParent(arguments);    
    },

    afterComponentLayout : function(width) {
        var me = this,
            needsScroll = me.needsScroll;
        
        me.callParent(arguments);
            
        if (needsScroll) {
            me.layout.overflowHandler.scrollToItem(me.activeTab);
        }    
        delete me.needsScroll;
    },

    
    onClick: function(e, target) {
        var me = this,
            tabPanel = me.tabPanel,
            tabEl, tab, isCloseClick, tabInfo;

        if (e.getTarget('.' + Ext.baseCSSPrefix + 'box-scroller')) {
            return;
        }

        if (me.orientation === 'vertical' && (Ext.isIE8 || Ext.isIE9) && Ext.isStrict) {
            tabInfo = me.getTabInfoFromPoint(e.getXY());
            tab = tabInfo.tab;
            isCloseClick = tabInfo.close;
        } else {
            
            tabEl = e.getTarget('.' + Ext.tab.Tab.prototype.baseCls);
            tab = tabEl && Ext.getCmp(tabEl.id);
            isCloseClick = tab && tab.closeEl && (target === tab.closeEl.dom);
        }

        if (isCloseClick) {
            e.preventDefault();
        }
        if (tab && tab.isDisabled && !tab.isDisabled()) {
            if (tab.closable && isCloseClick) {
                tab.onCloseClick();
            } else {
                if (tabPanel) {
                    
                    tabPanel.setActiveTab(tab.card);
                } else {
                    me.setActiveTab(tab);
                }
                tab.focus();
            }
        }
    },

    
    onMouseMove: function(e) {
        var me = this,
            overTab = me._overTab,
            tabInfo, tab;

        if (e.getTarget('.' + Ext.baseCSSPrefix + 'box-scroller')) {
            return;
        }

        tabInfo = me.getTabInfoFromPoint(e.getXY());
        tab = tabInfo.tab;

        if (tab !== overTab) {
            if (overTab && overTab.rendered) {
                overTab.onMouseLeave(e);
                me._overTab = null;
            }
            if (tab) {
                tab.onMouseEnter(e);
                me._overTab = tab;
                if (!tab.disabled) {
                    me.el.setStyle('cursor', 'pointer');
                }
            } else {
                me.el.setStyle('cursor', 'default');
            }
        }
    },

    onMouseLeave: function(e) {
        var overTab = this._overTab;

        if (overTab && overTab.rendered) {
            overTab.onMouseLeave(e);
        }
    },

    
    
    
    
    
    
    getTabInfoFromPoint: function(xy) {
        var me = this,
            tabs = me.items.items,
            length = tabs.length,
            innerCt = me.layout.innerCt,
            innerCtXY = innerCt.getXY(),
            point = new Ext.util.Point(xy[0], xy[1]),
            i = 0,
            lastBox, tabRegion, closeEl, close, closeXY, closeX, closeY, closeWidth,
            closeHeight, tabX, tabY, tabWidth, tabHeight, closeRegion, isTabReversed,
            direction, tab;

        for (; i < length; i++) {
            lastBox = tabs[i].lastBox;
            tabX = innerCtXY[0] + lastBox.x;
            tabY = innerCtXY[1] - innerCt.dom.scrollTop + lastBox.y;
            tabWidth = lastBox.width;
            tabHeight = lastBox.height;
            tabRegion = new Ext.util.Region(
                tabY,
                tabX + tabWidth,
                tabY + tabHeight,
                tabX
            );
            if (tabRegion.contains(point)) {
                tab = tabs[i];
                closeEl = tab.closeEl;
                if (closeEl) {
                    
                    
                    
                    if (me._isTabReversed === undefined) {
                        me._isTabReversed = isTabReversed =
                        
                        
                        (tab.btnWrap.dom.currentStyle.filter.indexOf('rotation=2') !== -1);
                    }

                    direction = isTabReversed ? this._reverseDockNames[me.dock] : me.dock;
                    
                    closeWidth = closeEl.getWidth();
                    closeHeight = closeEl.getHeight();
                    closeXY = me.getCloseXY(closeEl, tabX, tabY, tabWidth, tabHeight,
                        closeWidth, closeHeight, direction);
                    closeX = closeXY[0];
                    closeY = closeXY[1];

                    closeRegion = new Ext.util.Region(
                        closeY,
                        closeX + closeWidth,
                        closeY + closeHeight,
                        closeX
                    );

                    close = closeRegion.contains(point);
                }
                break;
            }
        }
            
        return {
            tab: tab,
            close: close
        };
    },

    
    getCloseXY: function(closeEl, tabX, tabY, tabWidth, tabHeight, closeWidth, closeHeight, direction) {
        var closeXY = closeEl.getXY(),
            closeX, closeY;

        if (direction === 'right') {
            closeX = tabX + tabWidth - ((closeXY[1] - tabY) + closeHeight); 
            closeY = tabY + (closeXY[0] - tabX); 
        } else {
            closeX = tabX + (closeXY[1] - tabY);
            closeY = tabY + tabX + tabHeight - closeXY[0] - closeWidth;
        }

        return [closeX, closeY];
    },

    
    closeTab: function(toClose) {
        var me = this,
            card = toClose.card,
            tabPanel = me.tabPanel,
            toActivate;

        if (card && card.fireEvent('beforeclose', card) === false) {
            return false;
        }
        
        
        
        
        toActivate = me.findNextActivatable(toClose);

        
        
        Ext.suspendLayouts();

        if (tabPanel && card) {
            
            
            delete toClose.ownerCt;
            
            
            
            card.fireEvent('close', card);
            tabPanel.remove(card);
            
            
            if (!tabPanel.getComponent(card)) {
                
                toClose.fireClose();
                me.remove(toClose);
            } else {
                
                toClose.ownerCt = me;
                Ext.resumeLayouts(true);
                return false;
            }
        }

        
        if (toActivate) {
            
            
            if (tabPanel) {
                tabPanel.setActiveTab(toActivate.card);
            } else {
                me.setActiveTab(toActivate);
            }
            toActivate.focus();
        }
        Ext.resumeLayouts(true);
    },

    
    
    findNextActivatable: function(toClose) {
        var me = this;
        if (toClose.active && me.items.getCount() > 1) {
            return (me.previousTab && me.previousTab !== toClose && !me.previousTab.disabled) ? me.previousTab : (toClose.next('tab[disabled=false]') || toClose.prev('tab[disabled=false]'));
        }
    },

    
    setActiveTab: function(tab, initial) {
        var me = this;

        if (!tab.disabled && tab !== me.activeTab) {
            if (me.activeTab) {
                if (me.activeTab.isDestroyed) {
                    me.previousTab = null;
                } else {
                    me.previousTab = me.activeTab;
                    me.activeTab.deactivate();
                }
            }
            tab.activate();

            me.activeTab = tab;
            me.needsScroll = true;
            
            
            
            if (!initial) {
                me.fireEvent('change', me, tab, tab.card);
                
                me.updateLayout();
            }
        }
    }
});


Ext.define('Ext.tree.Column', {
    extend:  Ext.grid.column.Column ,
    alias: 'widget.treecolumn',

    tdCls: Ext.baseCSSPrefix + 'grid-cell-treecolumn',

    autoLock: true,
    lockable: false,
    draggable: false,
    hideable: false,

    iconCls: Ext.baseCSSPrefix + 'tree-icon',
    checkboxCls: Ext.baseCSSPrefix + 'tree-checkbox',
    elbowCls: Ext.baseCSSPrefix + 'tree-elbow',
    expanderCls: Ext.baseCSSPrefix + 'tree-expander',
    textCls: Ext.baseCSSPrefix + 'tree-node-text',
    innerCls: Ext.baseCSSPrefix + 'grid-cell-inner-treecolumn',
    isTreeColumn: true,

    cellTpl: [
        '<tpl for="lines">',
            '<img src="{parent.blankUrl}" class="{parent.childCls} {parent.elbowCls}-img ',
            '{parent.elbowCls}-<tpl if=".">line<tpl else>empty</tpl>" role="presentation"/>',
        '</tpl>',
        '<img src="{blankUrl}" class="{childCls} {elbowCls}-img {elbowCls}',
            '<tpl if="isLast">-end</tpl><tpl if="expandable">-plus {expanderCls}</tpl>" role="presentation"/>',
        '<tpl if="checked !== null">',
            '<input type="button" {ariaCellCheckboxAttr}',
                ' class="{childCls} {checkboxCls}<tpl if="checked"> {checkboxCls}-checked</tpl>"/>',
        '</tpl>',
        '<img src="{blankUrl}" role="presentation" class="{childCls} {baseIconCls} ',
            '{baseIconCls}-<tpl if="leaf">leaf<tpl else>parent</tpl> {iconCls}"',
            '<tpl if="icon">style="background-image:url({icon})"</tpl>/>',
        '<tpl if="href">',
            '<a href="{href}" role="link" target="{hrefTarget}" class="{textCls} {childCls}">{value}</a>',
        '<tpl else>',
            '<span class="{textCls} {childCls}">{value}</span>',
        '</tpl>'
    ],

    initComponent: function() {
        var me = this,
            renderer = me.renderer;

        if (typeof renderer == 'string') {
            renderer = Ext.util.Format[renderer];
        }
        me.origRenderer = renderer;
        me.origScope = me.scope || window;

        me.renderer = me.treeRenderer;
        me.scope = me;

        me.callParent();
    },

    treeRenderer: function(value, metaData, record, rowIdx, colIdx, store, view){
        var me = this,
            cls = record.get('cls'),
            rendererData;

        if (cls) {
            metaData.tdCls += ' ' + cls;
        }

        rendererData = me.initTemplateRendererData(value, metaData, record, rowIdx, colIdx, store, view);
        
        return me.getTpl('cellTpl').apply(rendererData);
    },
    
    initTemplateRendererData: function(value, metaData, record, rowIdx, colIdx, store, view) {
        var me = this,
            renderer = me.origRenderer,
            data = record.data,
            parent = record.parentNode,
            rootVisible = view.rootVisible,
            lines = [],
            parentData;
        
        while (parent && (rootVisible || parent.data.depth > 0)) {
            parentData = parent.data;
            lines[rootVisible ? parentData.depth : parentData.depth - 1] =
                    parentData.isLast ? 0 : 1;
            parent = parent.parentNode;
        }
        
        return {
            record: record,
            baseIconCls: me.iconCls,
            iconCls: data.iconCls,
            icon: data.icon,
            checkboxCls: me.checkboxCls,
            checked: data.checked,
            elbowCls: me.elbowCls,
            expanderCls: me.expanderCls,
            textCls: me.textCls,
            leaf: data.leaf,
            expandable: record.isExpandable(),
            isLast: data.isLast,
            blankUrl: Ext.BLANK_IMAGE_URL,
            href: data.href,
            hrefTarget: data.hrefTarget,
            lines: lines,
            metaData: metaData,
            
            
            
            
            childCls: me.getChildCls ? me.getChildCls() + ' ' : '',
            value: renderer ? renderer.apply(me.origScope, arguments) : value
        };
    }
});


Ext.define('Ext.selection.CheckboxModel', {
    alias: 'selection.checkboxmodel',
    extend:  Ext.selection.RowModel ,

    
    mode: 'MULTI',

    
    injectCheckbox: 0,

    
    checkOnly: false,
    
    
    showHeaderCheckbox: undefined,
    
    
    checkSelector: '.' + Ext.baseCSSPrefix + 'grid-row-checker',

    headerWidth: 24,

    
    checkerOnCls: Ext.baseCSSPrefix + 'grid-hd-checker-on',
    
    constructor: function(){
        var me = this;
        me.callParent(arguments);   
        
        
        
        if (me.mode === 'SINGLE' && me.showHeaderCheckbox !== true) {
            me.showHeaderCheckbox = false;
        } 
    },

    beforeViewRender: function(view) {
        var me = this,
            owner;
            
        me.callParent(arguments);

        
        if (!me.hasLockedHeader() || view.headerCt.lockedCt) {
            if (me.showHeaderCheckbox !== false) {
                view.headerCt.on('headerclick', me.onHeaderClick, me);
            }
            me.addCheckbox(view, true);
            owner = view.ownerCt;
            
            if (view.headerCt.lockedCt) {
                owner = owner.ownerCt;
            }
            me.mon(owner, 'reconfigure', me.onReconfigure, me);
        }
    },

    bindComponent: function(view) {
        var me = this;
        me.sortable = false;
        me.callParent(arguments);
    },

    hasLockedHeader: function(){
        var views     = this.views,
            vLen      = views.length,
            v;

        for (v = 0; v < vLen; v++) {
            if (views[v].headerCt.lockedCt) {
                return true;
            }
        }
        return false;
    },

    
    addCheckbox: function(view, initial){
        var me = this,
            checkbox = me.injectCheckbox,
            headerCt = view.headerCt;

        
        if (checkbox !== false) {
            if (checkbox == 'first') {
                checkbox = 0;
            } else if (checkbox == 'last') {
                checkbox = headerCt.getColumnCount();
            }
            Ext.suspendLayouts();
            if (view.getStore().buffered) {
                me.showHeaderCheckbox = false;
            }
            headerCt.add(checkbox,  me.getHeaderConfig());
            Ext.resumeLayouts();
        }

        if (initial !== true) {
            view.refresh();
        }
    },

    
    onReconfigure: function(grid, store, columns) {
        if(columns) {
            this.addCheckbox(this.views[0]);
        }
    },

    
    toggleUiHeader: function(isChecked) {
        var view     = this.views[0],
            headerCt = view.headerCt,
            checkHd  = headerCt.child('gridcolumn[isCheckerHd]'),
            cls = this.checkerOnCls;

        if (checkHd) {
            if (isChecked) {
                checkHd.addCls(cls);
            } else {
                checkHd.removeCls(cls);
            }
        }
    },

    
    onHeaderClick: function(headerCt, header, e) {
        if (header.isCheckerHd) {
            e.stopEvent();
            var me = this,
                isChecked = header.el.hasCls(Ext.baseCSSPrefix + 'grid-hd-checker-on');
                
            
            me.preventFocus = true;
            if (isChecked) {
                me.deselectAll();
            } else {
                me.selectAll();
            }
            delete me.preventFocus;
        }
    },

    
    getHeaderConfig: function() {
        var me = this,
            showCheck = me.showHeaderCheckbox !== false;     

        return {
            isCheckerHd: showCheck,
            text : '&#160;',
            clickTargetName: 'el',
            width: me.headerWidth,
            sortable: false,
            draggable: false,
            resizable: false,
            hideable: false,
            menuDisabled: true,
            dataIndex: '',
            cls: showCheck ? Ext.baseCSSPrefix + 'column-header-checkbox ' : '',
            renderer: Ext.Function.bind(me.renderer, me),
            editRenderer: me.editRenderer || me.renderEmpty,
            locked: me.hasLockedHeader()
        };
    },

    renderEmpty: function() {
        return '&#160;';
    },

    
    refresh: function() {
        this.callParent(arguments);
        this.updateHeaderState();
    },

    
    renderer: function(value, metaData, record, rowIndex, colIndex, store, view) {
        var baseCSSPrefix = Ext.baseCSSPrefix;
        metaData.tdCls = baseCSSPrefix + 'grid-cell-special ' + baseCSSPrefix + 'grid-cell-row-checker';
        return '<div class="' + baseCSSPrefix + 'grid-row-checker" role="presentation">&#160;</div>';
    },
    
    processSelection: function(view, record, item, index, e){
        var me = this,
            checker = e.getTarget(me.checkSelector),
            mode;
            
        
        if (me.checkOnly && !checker) {
            return;
        }

        if (checker) {
            mode = me.getSelectionMode();
            
            
            if (mode !== 'SINGLE') {
                me.setSelectionMode('SIMPLE');
            }
            me.selectWithEvent(record, e);
            me.setSelectionMode(mode);
        } else {
            me.selectWithEvent(record, e);
        }
    },

    
    onSelectChange: function() {
        this.callParent(arguments);
        if (!this.suspendChange) {
            this.updateHeaderState();
        }
    },

    
    onStoreLoad: function() {
        this.callParent(arguments);
        this.updateHeaderState();
    },

    onStoreAdd: function() {
        this.callParent(arguments);
        this.updateHeaderState();
    },

    onStoreRemove: function() {
        this.callParent(arguments);
        this.updateHeaderState();
    },
    
    onStoreRefresh: function(){
        this.callParent(arguments);    
        this.updateHeaderState();
    },
    
    maybeFireSelectionChange: function(fireEvent) {
        if (fireEvent && !this.suspendChange) {
            this.updateHeaderState();
        }
        this.callParent(arguments);
    },
    
    resumeChanges: function(){
        this.callParent();
        if (!this.suspendChange) {
            this.updateHeaderState();
        }
    },

    
    updateHeaderState: function() {
        
        var me = this,
            store = me.store,
            storeCount = store.getCount(),
            views = me.views,
            hdSelectStatus = false,
            selectedCount = 0,
            selected, len, i;
            
        if (!store.buffered && storeCount > 0) {
            selected = me.selected;
            hdSelectStatus = true;
            for (i = 0, len = selected.getCount(); i < len; ++i) {
                if (!me.storeHasSelected(selected.getAt(i))) {
                    break;
                }
                ++selectedCount;
            }
            hdSelectStatus = storeCount === selectedCount;
        }
            
        if (views && views.length) {
            me.toggleUiHeader(hdSelectStatus);
        }
    }
});


Ext.define('Ext.slider.Single', {
    extend:  Ext.slider.Multi ,
    alias: ['widget.slider', 'widget.sliderfield'],
    alternateClassName: ['Ext.Slider', 'Ext.form.SliderField', 'Ext.slider.SingleSlider', 'Ext.slider.Slider'],

    
    getValue: function() {
        
        return this.callParent([0]);
    },

    
    setValue: function(value, animate) {
        var args = arguments,
            len  = args.length;

        
        
        
        if (len == 1 || (len <= 3 && typeof args[1] != 'number')) {
            args = Ext.toArray(args);
            args.unshift(0);
        }
        return this.callParent(args);
    },

    
    getNearest : function(){
        
        return this.thumbs[0];
    }
});


Ext.define('Ext.state.CookieProvider', {
    extend:  Ext.state.Provider ,

    

    

    

    

    
    constructor : function(config){
        var me = this;
        me.path = "/";
        me.expires = new Date(Ext.Date.now() + (1000*60*60*24*7)); 
        me.domain = null;
        me.secure = false;
        me.callParent(arguments);
        me.state = me.readCookies();
    },

    
    set : function(name, value){
        var me = this;

        if(typeof value == "undefined" || value === null){
            me.clear(name);
            return;
        }
        me.setCookie(name, value);
        me.callParent(arguments);
    },

    
    clear : function(name){
        this.clearCookie(name);
        this.callParent(arguments);
    },

    
    readCookies : function(){
        var cookies = {},
            c = document.cookie + ";",
            re = /\s?(.*?)=(.*?);/g,
            prefix = this.prefix,
            len = prefix.length,
            matches,
            name,
            value;

        while((matches = re.exec(c)) != null){
            name = matches[1];
            value = matches[2];
            if (name && name.substring(0, len) == prefix){
                cookies[name.substr(len)] = this.decodeValue(value);
            }
        }
        return cookies;
    },

    
    setCookie : function(name, value){
        var me = this;

        document.cookie = me.prefix + name + "=" + me.encodeValue(value) +
           ((me.expires == null) ? "" : ("; expires=" + me.expires.toGMTString())) +
           ((me.path == null) ? "" : ("; path=" + me.path)) +
           ((me.domain == null) ? "" : ("; domain=" + me.domain)) +
           ((me.secure == true) ? "; secure" : "");
    },

    
    clearCookie : function(name){
        var me = this;

        document.cookie = me.prefix + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
           ((me.path == null) ? "" : ("; path=" + me.path)) +
           ((me.domain == null) ? "" : ("; domain=" + me.domain)) +
           ((me.secure == true) ? "; secure" : "");
    }
});


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 {

                ++instance._users;
            }

            return instance;
        },

        
        supported: true
    },

    constructor: function (config) {
        var me = this;

        Ext.apply(me, config);


        if (me._users) {
            
            
            
            Ext.util.LocalStorage.cache[me.id] = me;
        }

        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;


        delete Ext.util.LocalStorage.cache[me.id];
        me._store = me._keys = null;
        me.destroyed = true;
        me.destroy = Ext.emptyFn;
    },

    
    getKeys: function () {
        var me = this,
            store = me._store,
            prefix = me.prefix,
            keys = me._keys,
            n = prefix.length,
            i, key;

        if (!keys) {
            me._keys = keys = [];

            for (i = store.length; i--; ) {
                key = store.key(i);
                if (key.length > n) {
                    if (prefix === key.substring(0, n)) {
                        keys.push(key.substring(n));
                    }
                }
            }
        }

        return keys;
    },

    
    release: function () {
        if (! --this._users) {
            this.destroy();
        }
    },

    save: Ext.emptyFn,

    
    clear: function () {
        var me = this,
            store = me._store,
            prefix = me.prefix,
            keys = me._keys || me.getKeys(),
            i;

        for (i = keys.length; i--; ) {
            store.removeItem(prefix + keys[i]);
        }

        keys.length = 0;
    },

    
    key: function (index) {
        var keys = this._keys || this.getKeys();

        return (0 <= index && index < keys.length) ? keys[index] : null;
    },

    
    getItem: function (key) {
        var k = this.prefix + key;

        return this._store.getItem(k);
    },

    
    removeItem: function (key) {
        var me = this,
            k = me.prefix + key,
            store = me._store,
            keys = me._keys,
            length = store.length;

        store.removeItem(k);

        if (keys && length !== store.length) {
            if (me.lazyKeys) {
                me._keys = null;
            } else {
                Ext.Array.remove(keys, key);
            }
        }
    },

    
    setItem: function (key, value) {
        var me = this,
            k = me.prefix + key,
            store = me._store,
            length = store.length,
            keys = me._keys;

        store.setItem(k, value);

        if (keys && length !== store.length) {
            
            keys.push(key);
        }
    }
}, function () {
    var LocalStorage = this;

    if ('localStorage' in window) {
        return;
    }
    if (!Ext.isIE) {
        LocalStorage.supported = false;
        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 = setTimeout(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.state.LocalStorageProvider', {
    extend:  Ext.state.Provider ,
               
                               
      
    
    alias: 'state.localstorage',
   
    constructor: function () {
        var me = this;

        me.callParent(arguments);

        me.store = me.getStorageObject();
        if (me.store) {
            me.state = me.readLocalStorage();
        } else {
            me.state = {};
        }
    },
    
    readLocalStorage: function () {
        var store = this.store,
            data = {},
            keys = store.getKeys(),
            i = keys.length,
            key;
            
        while (i--) {
            key = keys[i];
            data[key] = this.decodeValue(store.getItem(key));
        }

        return data;
    },
    
    set: function (name, value) {
        var me = this;
        
        me.clear(name);
        if (value != null) { 
            me.store.setItem(name, me.encodeValue(value));
            me.callParent(arguments);
        }
    },

    
    clear: function (name) {
        this.store.removeItem(name);
        this.callParent(arguments);
    },
    
    getStorageObject: function () {
        var prefix = this.prefix,
            id = prefix,
            n = id.length - 1;

        if (id.charAt(n) === '-') {
            id = id.substring(0, n);
        }

        return new Ext.util.LocalStorage({
            id: id,
            prefix: prefix
        });
    }    
});


Ext.define('Ext.tab.Panel', {
    extend:  Ext.panel.Panel ,
    alias: 'widget.tabpanel',
    alternateClassName: ['Ext.TabPanel'],

                                                           

    
    tabPosition : 'top',

    

    

    

    

    
    removePanelHeader: true,

    
    plain: false,

    
    itemCls: Ext.baseCSSPrefix + 'tabpanel-child',

    
    minTabWidth: undefined,

    
    maxTabWidth: undefined,

    
    deferredRender : true,

    
    initComponent: function() {
        var me = this,
            dockedItems = [].concat(me.dockedItems || []),
            activeTab = me.activeTab || (me.activeTab = 0),
            tabPosition = me.tabPosition;

        
        me.layout = new Ext.layout.container.Card(Ext.apply({
            owner: me,
            deferredRender: me.deferredRender,
            itemCls: me.itemCls,
            activeItem: activeTab
        }, me.layout));

        
        me.tabBar = new Ext.tab.Bar(Ext.apply({
            ui: me.ui,
            dock: me.tabPosition,
            orientation: (tabPosition == 'top' || tabPosition == 'bottom') ? 'horizontal' : 'vertical',
            plain: me.plain,
            cardLayout: me.layout,
            tabPanel: me
        }, me.tabBar));

        dockedItems.push(me.tabBar);
        me.dockedItems = dockedItems;

        me.addEvents(
            
            'beforetabchange',

            
            'tabchange'
        );

        me.callParent(arguments);

        
        activeTab = me.activeTab = me.getComponent(activeTab);

        
        if (activeTab) {
            me.tabBar.setActiveTab(activeTab.tab, true);
        }
    },

    
    setActiveTab: function(card) {
        var me = this,
            previous;

        card = me.getComponent(card);
        if (card) {
            previous = me.getActiveTab();

            if (previous === card || me.fireEvent('beforetabchange', me, card, previous) === false) {
                return false;
            }

            
            
            if (!card.isComponent) {
                Ext.suspendLayouts();
                card = me.add(card);
                Ext.resumeLayouts();
            }

            
            
            me.activeTab = card;

            
            
            
            Ext.suspendLayouts();
            me.layout.setActiveItem(card);

            
            card = me.activeTab = me.layout.getActiveItem();

            
            if (card && card !== previous) {

                
                me.tabBar.setActiveTab(card.tab);
                Ext.resumeLayouts(true);

                
                if (previous !== card) {
                    me.fireEvent('tabchange', me, card, previous);
                }
            }
            
            else {
                Ext.resumeLayouts(true);
            }
            return card;
        }
    },

    
    getActiveTab: function() {
        var me = this,
            
            result = me.getComponent(me.activeTab);

        
        if (result && me.items.indexOf(result) != -1) {
            me.activeTab = result;
        } else {
            me.activeTab = null;
        }

        return me.activeTab;
    },

    
    getTabBar: function() {
        return this.tabBar;
    },

    
    onAdd: function(item, index) {
        var me = this,
            cfg = item.tabConfig || {},
            defaultConfig = {
                xtype: 'tab',
                ui: me.tabBar.ui,
                card: item,
                disabled: item.disabled,
                closable: item.closable,
                hidden: item.hidden && !item.hiddenByLayout, 
                tooltip: item.tooltip,
                tabBar: me.tabBar,
                position: me.tabPosition
            };
        
        if (item.closeText !== undefined) {
            defaultConfig.closeText = item.closeText;
        }

        cfg = Ext.applyIf(cfg, defaultConfig);

        
        item.tab = me.tabBar.insert(index, cfg);

        item.on({
            scope : me,
            enable: me.onItemEnable,
            disable: me.onItemDisable,
            beforeshow: me.onItemBeforeShow,
            iconchange: me.onItemIconChange,
            iconclschange: me.onItemIconClsChange,
            titlechange: me.onItemTitleChange
        });

        if (item.isPanel) {
            if (me.removePanelHeader) {
                if (item.rendered) {
                    if (item.header) {
                        item.header.hide();
                    }
                } else {
                    item.header = false;
                }
            }
            if (item.isPanel && me.border) {
                item.setBorder(false);
            }
        }
    },

    
    onItemEnable: function(item){
        item.tab.enable();
    },

    
    onItemDisable: function(item){
        item.tab.disable();
    },

    
    onItemBeforeShow: function(item) {
        if (item !== this.activeTab) {
            this.setActiveTab(item);
            return false;
        }
    },

    
    onItemIconChange: function(item, newIcon) {
        item.tab.setIcon(newIcon);
    },
    
    
    onItemIconClsChange: function(item, newIconCls) {
        item.tab.setIconCls(newIconCls);
    },

    
    onItemTitleChange: function(item, newTitle) {
        item.tab.setText(newTitle);
    },

    
    doRemove: function(item, autoDestroy) {
        var me = this,
            toActivate;

        
        if (me.destroying || me.items.getCount() == 1) {
            me.activeTab = null;
        }

        
        
        else if ((toActivate = me.tabBar.items.indexOf(me.tabBar.findNextActivatable(item.tab))) !== -1) {
             me.setActiveTab(toActivate);
        }
        this.callParent(arguments);

        
        delete item.tab.card;
        delete item.tab;
    },

    
    onRemove: function(item, destroying) {
        var me = this;

        item.un({
            scope : me,
            enable: me.onItemEnable,
            disable: me.onItemDisable,
            beforeshow: me.onItemBeforeShow
        });
        if (!me.destroying && item.tab.ownerCt === me.tabBar) {
            me.tabBar.remove(item.tab);
        }
    }
});


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


Ext.define('Ext.tree.Panel', {
    extend:  Ext.panel.Table ,
    alias: 'widget.treepanel',
    alternateClassName: ['Ext.tree.TreePanel', 'Ext.TreePanel'],
                                                                                                    
    viewType: 'treeview',
    selType: 'treemodel',

    treeCls: Ext.baseCSSPrefix + 'tree-panel',

    deferRowRender: false,

    
    rowLines: false,

    
    lines: true,

    
    useArrows: false,

    
    singleExpand: false,

    ddConfig: {
        enableDrag: true,
        enableDrop: true
    },

    

    
    rootVisible: true,

    
    displayField: 'text',

    
    root: null,

    
    
    normalCfgCopy: ['displayField', 'root', 'singleExpand', 'useArrows', 'lines', 'rootVisible', 'scroll'],
    lockedCfgCopy: ['displayField', 'root', 'singleExpand', 'useArrows', 'lines', 'rootVisible'],
    
    isTree: true,

    

    
     
    
    
    arrowCls: Ext.baseCSSPrefix + 'tree-arrows',
    linesCls: Ext.baseCSSPrefix + 'tree-lines',
    noLinesCls: Ext.baseCSSPrefix + 'tree-no-lines',
    autoWidthCls: Ext.baseCSSPrefix + 'autowidth-table',

    constructor: function(config) {
        config = config || {};
        if (config.animate === undefined) {
            config.animate = Ext.isBoolean(this.animate) ? this.animate : Ext.enableFx;
        }
        this.enableAnimations = config.animate;
        delete config.animate;

        this.callParent([config]);
    },

    initComponent: function() {
        var me = this,
            cls = [me.treeCls],
            store = me.store,
            view;

        if (me.useArrows) {
            cls.push(me.arrowCls);
            me.lines = false;
        }

        if (me.lines) {
            cls.push(me.linesCls);
        } else if (!me.useArrows) {
            cls.push(me.noLinesCls);
        }

        if (Ext.isString(store)) {
            store = me.store = Ext.StoreMgr.lookup(store);
        } else if (!store || !store.isStore) {
            store = Ext.apply({
                type: 'tree',
                root: me.root,
                fields: me.fields,
                model: me.model,
                folderSort: me.folderSort
            }, store);
            store = me.store = Ext.StoreMgr.lookup(store);
        } else if (me.root) {
            store = me.store = Ext.data.StoreManager.lookup(store);
            store.setRootNode(me.root);
            if (me.folderSort !== undefined) {
                store.folderSort = me.folderSort;
                store.sort();
            }
        }

        
        
        
        

        me.viewConfig = Ext.apply({
            rootVisible: me.rootVisible,
            animate: me.enableAnimations,
            singleExpand: me.singleExpand,
            node: store.getRootNode(),
            hideHeaders: me.hideHeaders
        }, me.viewConfig);

        
        if (!me.columns) {
            if (me.initialConfig.hideHeaders === undefined) {
                me.hideHeaders = true;
            }
            me.addCls(me.autoWidthCls);
            me.columns = [{
                xtype    : 'treecolumn',
                text     : 'Name',
                width    : Ext.isIE6 ? '100%' : 10000, 
                dataIndex: me.displayField         
            }];
        }

        if (me.cls) {
            cls.push(me.cls);
        }
        me.cls = cls.join(' ');

        me.callParent();

        
        
        me.selModel.treeStore = me.store;

        view = me.getView();

        
        
        me.relayEvents(view, [
            
            'checkchange',
            
            'afteritemexpand',
            
            'afteritemcollapse'
        ]);

        
        if (!view.isLockingView) {
            
            if (!view.rootVisible && !me.getRootNode()) {
                me.setRootNode({
                    expanded: true
                });
            }
        }
    },

    
    
    
    
    bindStore: function(store, initial) {
        var me = this;

        me.store = store;

        
        me.storeListeners = me.mon(store, {
            destroyable: true,
            load: me.onStoreLoad,
            rootchange: me.onRootChange,
            clear: me.onClear,
            scope: me
        });

        
        me.storeRelayers = me.relayEvents(store, [
            
            'beforeload',

            
            'load'
        ]);

        
        me.storeRelayers1 = me.mon(store, {
            destroyable: true,

            
            append: me.createRelayer('itemappend'),

            
            remove: me.createRelayer('itemremove'),

            
            move: me.createRelayer('itemmove', [0, 4]),

            
            insert: me.createRelayer('iteminsert'),

            
            beforeappend: me.createRelayer('beforeitemappend'),

            
            beforeremove: me.createRelayer('beforeitemremove'),

            
            beforemove: me.createRelayer('beforeitemmove'),

            
            beforeinsert: me.createRelayer('beforeiteminsert'),

            
            expand: me.createRelayer('itemexpand', [0, 1]),

            
            collapse: me.createRelayer('itemcollapse', [0, 1]),

            
            beforeexpand: me.createRelayer('beforeitemexpand', [0, 1]),

            
            beforecollapse: me.createRelayer('beforeitemcollapse', [0, 1])
        });

        
        store.ownerTree = me;
        
        if (!initial) {
            me.view.setRootNode(me.getRootNode());
        }
    },

    
    
    unbindStore: function() {
        var me = this,
            store = me.store;

        if (store) {
            Ext.destroy(me.storeListeners, me.storeRelayers, me.storeRelayers1);
            delete store.ownerTree;
        }
    },

    onClear: function(){
        this.view.onClear();
    },

    
    setRootNode: function() {
        return this.store.setRootNode.apply(this.store, arguments);
    },

    
    getRootNode: function() {
        return this.store.getRootNode();
    },

    onRootChange: function(root) {
        this.view.setRootNode(root);
    },

    
    getChecked: function() {
        return this.getView().getChecked();
    },

    isItemChecked: function(rec) {
        return rec.get('checked');
    },
    
    
    expandNode: function(record, deep, callback, scope) {
        return this.getView().expand(record, deep, callback, scope || this);
    },

    
    collapseNode: function(record, deep, callback, scope) {
        return this.getView().collapse(record, deep, callback, scope || this);
    },

    
    expandAll : function(callback, scope) {
        var me = this,
            root = me.getRootNode(),
            animate = me.enableAnimations;
        if (root) {
            if (!animate) {
                Ext.suspendLayouts();
            }
            root.expand(true, callback, scope || me);
            if (!animate) {
                Ext.resumeLayouts(true);
            }
        }
    },

    
    collapseAll : function(callback, scope) {
        var me = this,
            root = me.getRootNode(),
            animate = me.enableAnimations,
            view = me.getView();

        if (root) {
            if (!animate) {
                Ext.suspendLayouts();
            }
            scope = scope || me;
            if (view.rootVisible) {
                root.collapse(true, callback, scope);
            } else {
                root.collapseChildren(true, callback, scope);
            }
            if (!animate) {
                Ext.resumeLayouts(true);
            }
        }
    },

    
    expandPath: function(path, field, separator, callback, scope) {
        var me = this,
            current = me.getRootNode(),
            index = 1,
            view = me.getView(),
            keys,
            expander;

        field = field || me.getRootNode().idProperty;
        separator = separator || '/';

        if (Ext.isEmpty(path)) {
            Ext.callback(callback, scope || me, [false, null]);
            return;
        }

        keys = path.split(separator);
        if (current.get(field) != keys[1]) {
            
            Ext.callback(callback, scope || me, [false, current]);
            return;
        }

        expander = function(){
            if (++index === keys.length) {
                Ext.callback(callback, scope || me, [true, current]);
                return;
            }
            var node = current.findChild(field, keys[index]);
            if (!node) {
                Ext.callback(callback, scope || me, [false, current]);
                return;
            }
            current = node;
            current.expand(false, expander);
        };
        current.expand(false, expander);
    },

    
    selectPath: function(path, field, separator, callback, scope) {
        var me = this,
            root,
            keys,
            last;

        field = field || me.getRootNode().idProperty;
        separator = separator || '/';

        keys = path.split(separator);
        last = keys.pop();
        if (keys.length > 1) {
            me.expandPath(keys.join(separator), field, separator, function(success, node){
                var lastNode = node;
                if (success && node) {
                    node = node.findChild(field, last);
                    if (node) {
                        me.getSelectionModel().select(node);
                        Ext.callback(callback, scope || me, [true, node]);
                        return;
                    }
                }
                Ext.callback(callback, scope || me, [false, lastNode]);
            }, me);
        } else {
            root = me.getRootNode();
            if (root.getId() === last) {
                me.getSelectionModel().select(root);
                Ext.callback(callback, scope || me, [true, root]);
            } else {
                Ext.callback(callback, scope || me, [false, null]);
            }
        }
    }
});


Ext.define('Ext.view.DragZone', {
    extend:  Ext.dd.DragZone ,
    containerScroll: false,

    constructor: function(config) {
        var me = this,
            view,
            ownerCt,
            el;

        Ext.apply(me, config);

        
        
        
        
        
        if (!me.ddGroup) {
            me.ddGroup = 'view-dd-zone-' + me.view.id;
        }

        
        
        
        
        
        
        
        view = me.view;
        ownerCt = view.ownerCt;
        
        
        if (ownerCt) {
            el = ownerCt.getTargetEl().dom;
        } else {
            el = view.el.dom.parentNode;
        }
        me.callParent([el]);

        me.ddel = Ext.get(document.createElement('div'));
        me.ddel.addCls(Ext.baseCSSPrefix + 'grid-dd-wrap');
    },

    init: function(id, sGroup, config) {
        var me = this;
        
        me.initTarget(id, sGroup, config);
        me.view.on('itemmousedown', me.onItemMouseDown, me);
    },

    onValidDrop: function(target, e, id) {
        this.callParent();
        
        target.el.focus();
    },

    onItemMouseDown: function(view, record, item, index, e) {
        if (!this.isPreventDrag(e, record, item, index)) {
            
            
            
            if (view.focusRow) {
                view.focusRow(record);
            }
            this.handleMouseDown(e);
        }
    },

    
    isPreventDrag: function(e) {
        return false;
    },

    getDragData: function(e) {
        var view = this.view,
            item = e.getTarget(view.getItemSelector());

        if (item) {
            return {
                copy: view.copy || (view.allowCopy && e.ctrlKey),
                event: new Ext.EventObjectImpl(e),
                view: view,
                ddel: this.ddel,
                item: item,
                records: view.getSelectionModel().getSelection(),
                fromPosition: Ext.fly(item).getXY()
            };
        }
    },

    onInitDrag: function(x, y) {
        var me = this,
            data = me.dragData,
            view = data.view,
            selectionModel = view.getSelectionModel(),
            record = view.getRecord(data.item);

        
        
        if (!selectionModel.isSelected(record)) {
            selectionModel.selectWithEvent(record, me.DDMInstance.mousedownEvent);
        }
        data.records = selectionModel.getSelection();

        me.ddel.update(me.getDragText());
        me.proxy.update(me.ddel.dom);
        me.onStartDrag(x, y);
        return true;
    },

    getDragText: function() {
        var count = this.dragData.records.length;
        return Ext.String.format(this.dragText, count, count == 1 ? '' : 's');
    },

    getRepairXY : function(e, data){
        return data ? data.fromPosition : false;
    }
});


Ext.define('Ext.tree.ViewDragZone', {
    extend:  Ext.view.DragZone ,

    isPreventDrag: function(e, record) {
        return (record.get('allowDrag') === false) || !!e.getTarget(this.view.expanderSelector);
    },

    getDragText: function() {
        var records = this.dragData.records,
            count = records.length,
            text = records[0].get(this.displayField),
            suffix = 's';
            
        if (count === 1 && text) {
            return text;    
        } else if (!text) {
            suffix = '';
        }
        return Ext.String.format(this.dragText, count, suffix);
    },

    afterRepair: function() {
        var me = this,
            view = me.view,
            selectedRowCls = view.selectedItemCls,
            records = me.dragData.records,
            r,
            rLen    = records.length,
            fly = Ext.fly,
            item;

        if (Ext.enableFx && me.repairHighlight) {
            
            for (r = 0; r < rLen; r++) {
                
                
                item = view.getNode(records[r]);

                
                
                fly(item.firstChild).highlight(me.repairHighlightColor, {
                    listeners: {
                        beforeanimate: function() {
                            if (view.isSelected(item)) {
                                fly(item).removeCls(selectedRowCls);
                            }
                        },
                        afteranimate: function() {
                            if (view.isSelected(item)) {
                                fly(item).addCls(selectedRowCls);
                            }
                        }
                    }
                });
            }

        }
        me.dragging = false;
    }
});


Ext.define('Ext.tree.ViewDropZone', {
    extend:  Ext.view.DropZone ,

    
    allowParentInserts: false,
 
    
    allowContainerDrops: false,

    
    appendOnly: false,

    
    expandDelay : 500,

    indicatorCls: Ext.baseCSSPrefix + 'tree-ddindicator',

    
    expandNode : function(node) {
        var view = this.view;
        this.expandProcId = false;
        if (!node.isLeaf() && !node.isExpanded()) {
            view.expand(node);
            this.expandProcId = false;
        }
    },

    
    queueExpand : function(node) {
        this.expandProcId = Ext.Function.defer(this.expandNode, this.expandDelay, this, [node]);
    },

    
    cancelExpand : function() {
        if (this.expandProcId) {
            clearTimeout(this.expandProcId);
            this.expandProcId = false;
        }
    },

    getPosition: function(e, node) {
        var view = this.view,
            record = view.getRecord(node),
            y = e.getPageY(),
            noAppend = record.isLeaf(),
            noBelow = false,
            region = Ext.fly(node).getRegion(),
            fragment;

        
        if (record.isRoot()) {
            return 'append';
        }

        
        if (this.appendOnly) {
            return noAppend ? false : 'append';
        }

        if (!this.allowParentInserts) {
            noBelow = record.hasChildNodes() && record.isExpanded();
        }

        fragment = (region.bottom - region.top) / (noAppend ? 2 : 3);
        if (y >= region.top && y < (region.top + fragment)) {
            return 'before';
        }
        else if (!noBelow && (noAppend || (y >= (region.bottom - fragment) && y <= region.bottom))) {
            return 'after';
        }
        else {
            return 'append';
        }
    },

    isValidDropPoint : function(node, position, dragZone, e, data) {
        if (!node || !data.item) {
            return false;
        }

        var view = this.view,
            targetNode = view.getRecord(node),
            draggedRecords = data.records,
            dataLength = draggedRecords.length,
            ln = draggedRecords.length,
            i, record;

        
        if (!(targetNode && position && dataLength)) {
            return false;
        }

        
        for (i = 0; i < ln; i++) {
            record = draggedRecords[i];
            if (record.isNode && record.contains(targetNode)) {
                return false;
            }
        }
        
        
        if (position === 'append' && targetNode.get('allowDrop') === false) {
            return false;
        }
        else if (position != 'append' && targetNode.parentNode.get('allowDrop') === false) {
            return false;
        }

        
        if (Ext.Array.contains(draggedRecords, targetNode)) {
             return false;
        }
        return view.fireEvent('nodedragover', targetNode, position, data, e) !== false;
    },

    onNodeOver : function(node, dragZone, e, data) {
        var position = this.getPosition(e, node),
            returnCls = this.dropNotAllowed,
            view = this.view,
            targetNode = view.getRecord(node),
            indicator = this.getIndicator(),
            indicatorY = 0;

        
        this.cancelExpand();
        if (position == 'append' && !this.expandProcId && !Ext.Array.contains(data.records, targetNode) && !targetNode.isLeaf() && !targetNode.isExpanded()) {
            this.queueExpand(targetNode);
        }
            
            
        if (this.isValidDropPoint(node, position, dragZone, e, data)) {
            this.valid = true;
            this.currentPosition = position;
            this.overRecord = targetNode;

            indicator.setWidth(Ext.fly(node).getWidth());
            indicatorY = Ext.fly(node).getY() - Ext.fly(view.el).getY() - 1;

            
            if (position == 'before') {
                returnCls = targetNode.isFirst() ? Ext.baseCSSPrefix + 'tree-drop-ok-above' : Ext.baseCSSPrefix + 'tree-drop-ok-between';
                indicator.showAt(0, indicatorY);
                dragZone.proxy.show();
            } else if (position == 'after') {
                returnCls = targetNode.isLast() ? Ext.baseCSSPrefix + 'tree-drop-ok-below' : Ext.baseCSSPrefix + 'tree-drop-ok-between';
                indicatorY += Ext.fly(node).getHeight();
                indicator.showAt(0, indicatorY);
                dragZone.proxy.show();
            } else {
                returnCls = Ext.baseCSSPrefix + 'tree-drop-ok-append';
                
                indicator.hide();
            }
        } else {
            this.valid = false;
        }

        this.currentCls = returnCls;
        return returnCls;
    },

    
    onNodeOut : function(n, dd, e, data){
        this.valid = false;
        this.getIndicator().hide();
    },

    onContainerOver : function(dd, e, data) {
        return e.getTarget('.' + this.indicatorCls) ? this.currentCls : this.dropNotAllowed;
    },
    
    notifyOut: function() {
        this.callParent(arguments);
        this.cancelExpand();
    },

    handleNodeDrop : function(data, targetNode, position) {
        var me = this,
            targetView = me.view,
            parentNode = targetNode ? targetNode.parentNode : targetView.panel.getRootNode(),
            Model = targetView.getStore().treeStore.model,
            records, i, len, record,
            insertionMethod, argList,
            needTargetExpand,
            transferData;

        
        if (data.copy) {
            records = data.records;
            data.records = [];
            for (i = 0, len = records.length; i < len; i++) {
                record = records[i];
                if (record.isNode) {
                    data.records.push(record.copy(undefined, true));
                } else {
                    
                    data.records.push(new Model(record.data, record.getId()));
                }
            }
        }

        
        me.cancelExpand();

        
        
        
        
        if (position == 'before') {
            insertionMethod = parentNode.insertBefore;
            argList = [null, targetNode];
            targetNode = parentNode;
        }
        else if (position == 'after') {
            if (targetNode.nextSibling) {
                insertionMethod = parentNode.insertBefore;
                argList = [null, targetNode.nextSibling];
            }
            else {
                insertionMethod = parentNode.appendChild;
                argList = [null];
            }
            targetNode = parentNode;
        }
        else {
            if (!(targetNode.isExpanded() || targetNode.isLoading())) {
                needTargetExpand = true;
            }
            insertionMethod = targetNode.appendChild;
            argList = [null];
        }
        
        
        transferData = function() {
            var color,
                n;

            
            Ext.suspendLayouts();

            
            for (i = 0, len = data.records.length; i < len; i++) {
                record = data.records[i];
                if (!record.isNode) {
                    if (record.isModel) {
                        record = new Model(record.data, record.getId());
                    } else {
                        record = new Model(record);
                    }
                    data.records[i] = record;
                }
                argList[0] = record;
                insertionMethod.apply(targetNode, argList);
            }

            
            if (me.sortOnDrop) {
                targetNode.sort(targetNode.getOwnerTree().store.generateComparator());
            }
            
            Ext.resumeLayouts(true);

            
            
            
            if (Ext.enableFx && me.dropHighlight) {
                color = me.dropHighlightColor;

                for (i = 0; i < len; i++) {
                    n = targetView.getNode(data.records[i]);
                    if (n) {
                        Ext.fly(n).highlight(color);
                    }
                }
            }
        };

        
        if (needTargetExpand) {
            targetNode.expand(false, transferData);
        }
        
        
        
        
        
        else if (targetNode.isLoading()) {
            targetNode.on({
                expand: transferData,
                delay: 1,
                single: true
            });
        }
        
        else {
            transferData();
        }
    }    
});


Ext.define('Ext.tree.plugin.TreeViewDragDrop', {
    extend:  Ext.AbstractPlugin ,
    alias: 'plugin.treeviewdragdrop',

           
                                
                               
      

    

    

    
    
    dragText : '{0} selected node{1}',
    

    
    allowParentInserts: false,

    
    allowContainerDrops: false,

    
    appendOnly: false,

    
    ddGroup : "TreeDD",
    
    
    containerScroll: false,

    

    

    

    
    expandDelay : 1000,

    
    enableDrop: true,

    
    enableDrag: true,

    
    nodeHighlightColor: 'c3daf9',

    
    nodeHighlightOnDrop: Ext.enableFx,

    
    nodeHighlightOnRepair: Ext.enableFx,
    
    
    displayField: 'text',

    init : function(view) {
        view.on('render', this.onViewRender, this, {single: true});
    },

    
    destroy: function() {
        Ext.destroy(this.dragZone, this.dropZone);
    },

    onViewRender : function(view) {
        var me = this,
            scrollEl;

        if (me.enableDrag) {
            if (me.containerScroll) {
                scrollEl = view.getEl();
            }
            me.dragZone = new Ext.tree.ViewDragZone({
                view: view,
                ddGroup: me.dragGroup || me.ddGroup,
                dragText: me.dragText,
                displayField: me.displayField,
                repairHighlightColor: me.nodeHighlightColor,
                repairHighlight: me.nodeHighlightOnRepair,
                scrollEl: scrollEl
            });
        }

        if (me.enableDrop) {
            me.dropZone = new Ext.tree.ViewDropZone({
                view: view,
                ddGroup: me.dropGroup || me.ddGroup,
                allowContainerDrops: me.allowContainerDrops,
                appendOnly: me.appendOnly,
                allowParentInserts: me.allowParentInserts,
                expandDelay: me.expandDelay,
                dropHighlightColor: me.nodeHighlightColor,
                dropHighlight: me.nodeHighlightOnDrop,
                sortOnDrop: me.sortOnDrop,
                containerScroll: me.containerScroll
            });
        }
    }
}, function(){
    var proto = this.prototype;
    proto.nodeHighlightOnDrop = proto.nodeHighlightOnRepair = Ext.enableFx;
});


Ext.define('Ext.util.Cookies', {
    singleton: true,
    
    
    set : function(name, value){
        var argv = arguments,
            argc = arguments.length,
            expires = (argc > 2) ? argv[2] : null,
            path = (argc > 3) ? argv[3] : '/',
            domain = (argc > 4) ? argv[4] : null,
            secure = (argc > 5) ? argv[5] : false;
            
        document.cookie = name + "=" + escape(value) + ((expires === null) ? "" : ("; expires=" + expires.toGMTString())) + ((path === null) ? "" : ("; path=" + path)) + ((domain === null) ? "" : ("; domain=" + domain)) + ((secure === true) ? "; secure" : "");
    },

    
    get : function(name){
        var arg = name + "=",
            alen = arg.length,
            clen = document.cookie.length,
            i = 0,
            j = 0;
            
        while(i < clen){
            j = i + alen;
            if(document.cookie.substring(i, j) == arg){
                return this.getCookieVal(j);
            }
            i = document.cookie.indexOf(" ", i) + 1;
            if(i === 0){
                break;
            }
        }
        return null;
    },

    
    clear : function(name, path){
        if(this.get(name)){
            path = path || '/';
            document.cookie = name + '=' + '; expires=Thu, 01-Jan-70 00:00:01 GMT; path=' + path;
        }
    },
    
    
    getCookieVal : function(offset){
        var endstr = document.cookie.indexOf(";", offset);
        if(endstr == -1){
            endstr = document.cookie.length;
        }
        return unescape(document.cookie.substring(offset, endstr));
    }
});


 
Ext.define('Ext.util.Grouper', {

    

    extend:  Ext.util.Sorter ,

    
   
   isGrouper: true,

    
    getGroupString: function(instance) {
        return instance.get(this.property);
    }
});


Ext.define('Ext.util.History', {
    singleton: true,
    alternateClassName: 'Ext.History',
    mixins: {
        observable:  Ext.util.Observable 
    },

    
    useTopWindow: true,

    
    fieldId: Ext.baseCSSPrefix + 'history-field',
    
    iframeId: Ext.baseCSSPrefix + 'history-frame',

    constructor: function() {
        var me = this;
        me.oldIEMode = Ext.isIE7m || !Ext.isStrict && Ext.isIE8;
        me.iframe = null;
        me.hiddenField = null;
        me.ready = false;
        me.currentToken = null;
        me.mixins.observable.constructor.call(me);
    },

    getHash: function() {
        var href = window.location.href,
            i = href.indexOf("#");

        return i >= 0 ? href.substr(i + 1) : null;
    },

    setHash: function (hash) {
        var me = this,
            win = me.useTopWindow ? window.top : window;
        try {
            win.location.hash = hash;
        } catch (e) {
            
        }
    },

    doSave: function() {
        this.hiddenField.value = this.currentToken;
    },


    handleStateChange: function(token) {
        this.currentToken = token;
        this.fireEvent('change', token);
    },

    updateIFrame: function(token) {
        var html = '<html><body><div id="state" role="presentation">' +
                    Ext.util.Format.htmlEncode(token) +
                    '</div></body></html>',
            doc;

        try {
            doc = this.iframe.contentWindow.document;
            doc.open();
            doc.write(html);
            doc.close();
            return true;
        } catch (e) {
            return false;
        }
    },

    checkIFrame: function () {
        var me = this,
            contentWindow = me.iframe.contentWindow,
            doc, elem, oldToken, oldHash;

        if (!contentWindow || !contentWindow.document) {
            Ext.Function.defer(this.checkIFrame, 10, this);
            return;
        }

        doc = contentWindow.document;
        elem = doc.getElementById("state");
        oldToken = elem ? elem.innerText : null;
        oldHash = me.getHash();

        Ext.TaskManager.start({
            run: function () {
                var doc = contentWindow.document,
                    elem = doc.getElementById("state"),
                    newToken = elem ? elem.innerText : null,
                    newHash = me.getHash();

                if (newToken !== oldToken) {
                    oldToken = newToken;
                    me.handleStateChange(newToken);
                    me.setHash(newToken);
                    oldHash = newToken;
                    me.doSave();
                } else if (newHash !== oldHash) {
                    oldHash = newHash;
                    me.updateIFrame(newHash);
                }
            },
            interval: 50,
            scope: me
        });
        me.ready = true;
        me.fireEvent('ready', me);
    },

    startUp: function () {
        var me = this,
            hash;

        me.currentToken = me.hiddenField.value || this.getHash();

        if (me.oldIEMode) {
            me.checkIFrame();
        } else {
            hash = me.getHash();
            Ext.TaskManager.start({
                run: function () {
                    var newHash = me.getHash();
                    if (newHash !== hash) {
                        hash = newHash;
                        me.handleStateChange(hash);
                        me.doSave();
                    }
                },
                interval: 50,
                scope: me
            });
            me.ready = true;
            me.fireEvent('ready', me);
        }

    },

    
    init: function (onReady, scope) {
        var me = this,
            DomHelper = Ext.DomHelper;

        if (me.ready) {
            Ext.callback(onReady, scope, [me]);
            return;
        }

        if (!Ext.isReady) {
            Ext.onReady(function() {
                me.init(onReady, scope);
            });
            return;
        }

        
        me.hiddenField = Ext.getDom(me.fieldId);
        if (!me.hiddenField) {
            me.hiddenField = Ext.getBody().createChild({
                id: Ext.id(),
                tag: 'form',
                role: 'presentation',
                cls: Ext.baseCSSPrefix + 'hide-display',
                children: [{
                    tag: 'input',
                    type: 'hidden',
                    id: me.fieldId
                }]
            }, false, true).firstChild;
        }

        if (me.oldIEMode) {
            me.iframe = Ext.getDom(me.iframeId);
            if (!me.iframe) {
                me.iframe = DomHelper.append(me.hiddenField.parentNode, {
                    tag: 'iframe',
                    role: 'presentation',
                    id: me.iframeId,
                    src: Ext.SSL_SECURE_URL
                });
            }
        }

        me.addEvents(
            
            'ready',
            
            'change'
        );

        if (onReady) {
            me.on('ready', onReady, scope, {single: true});
        }
        me.startUp();
    },

    
    add: function (token, preventDup) {
        var me = this;

        if (preventDup !== false) {
            if (me.getToken() === token) {
                return true;
            }
        }

        if (me.oldIEMode) {
            return me.updateIFrame(token);
        } else {
            me.setHash(token);
            return true;
        }
    },

    
    back: function() {
        window.history.go(-1);
    },

    
    forward: function(){
        window.history.go(1);
    },

    
    getToken: function() {
        return this.ready ? this.currentToken : this.getHash();
    }
});
